Commit 3ceeda1c authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/cs53l30', 'asoc/topic/cygnus',...

Merge remote-tracking branches 'asoc/topic/cs53l30', 'asoc/topic/cygnus', 'asoc/topic/da7219' and 'asoc/topic/davinci' into asoc-next
BROADCOM Cygnus Audio I2S/TDM/SPDIF controller
Required properties:
- compatible : "brcm,cygnus-audio"
- #address-cells: 32bit valued, 1 cell.
- #size-cells: 32bit valued, 0 cell.
- reg : Should contain audio registers location and length
- reg-names: names of the registers listed in "reg" property
Valid names are "aud" and "i2s_in". "aud" contains a
set of DMA, I2S_OUT and SPDIF registers. "i2s_in" contains
a set of I2S_IN registers.
- clocks: PLL and leaf clocks used by audio ports
- assigned-clocks: PLL and leaf clocks
- assigned-clock-parents: parent clocks of the assigned clocks
(usually the PLL)
- assigned-clock-rates: List of clock frequencies of the
assigned clocks
- clock-names: names of 3 leaf clocks used by audio ports
Valid names are "ch0_audio", "ch1_audio", "ch2_audio"
- interrupts: audio DMA interrupt number
SSP Subnode properties:
- reg: The index of ssp port interface to use
Valid value are 0, 1, 2, or 3 (for spdif)
Example:
cygnus_audio: audio@180ae000 {
compatible = "brcm,cygnus-audio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x180ae000 0xafd>, <0x180aec00 0x1f8>;
reg-names = "aud", "i2s_in";
clocks = <&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
<&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
<&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
assigned-clocks = <&audiopll BCM_CYGNUS_AUDIOPLL>,
<&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
<&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
<&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
assigned-clock-parents = <&audiopll BCM_CYGNUS_AUDIOPLL>;
assigned-clock-rates = <1769470191>,
<0>,
<0>,
<0>;
clock-names = "ch0_audio", "ch1_audio", "ch2_audio";
interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
ssp0: ssp_port@0 {
reg = <0>;
status = "okay";
};
ssp1: ssp_port@1 {
reg = <1>;
status = "disabled";
};
ssp2: ssp_port@2 {
reg = <2>;
status = "disabled";
};
spdif: spdif_port@3 {
reg = <3>;
status = "disabled";
};
};
CS53L30 audio CODEC
Required properties:
- compatible : "cirrus,cs53l30"
- reg : the I2C address of the device
- VA-supply, VP-supply : power supplies for the device,
as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
Optional properties:
- reset-gpios : a GPIO spec for the reset pin.
- mute-gpios : a GPIO spec for the MUTE pin. The active state can be either
GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW, which would be handled
by the driver automatically.
- cirrus,micbias-lvl : Set the output voltage level on the MICBIAS Pin.
0 = Hi-Z
1 = 1.80 V
2 = 2.75 V
- cirrus,use-sdout2 : This is a boolean property. If present, it indicates
the hardware design connects both SDOUT1 and SDOUT2
pins to output data. Otherwise, it indicates that
only SDOUT1 is connected for data output.
* CS53l30 supports 4-channel data output in the same
* frame using two different ways:
* 1) Normal I2S mode on two data pins -- each SDOUT
* carries 2-channel data in the same time.
* 2) TDM mode on one signle data pin -- SDOUT1 carries
* 4-channel data per frame.
Example:
codec: cs53l30@48 {
compatible = "cirrus,cs53l30";
reg = <0x48>;
reset-gpios = <&gpio 54 0>;
VA-supply = <&cs53l30_va>;
VP-supply = <&cs53l30_vp>;
};
...@@ -887,6 +887,34 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, ...@@ -887,6 +887,34 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
} }
EXPORT_SYMBOL_GPL(device_get_next_child_node); EXPORT_SYMBOL_GPL(device_get_next_child_node);
/**
* device_get_named_child_node - Return first matching named child node handle
* @dev: Device to find the named child node for.
* @childname: String to match child node name against.
*/
struct fwnode_handle *device_get_named_child_node(struct device *dev,
const char *childname)
{
struct fwnode_handle *child;
/*
* Find first matching named child node of this device.
* For ACPI this will be a data only sub-node.
*/
device_for_each_child_node(dev, child) {
if (is_of_node(child)) {
if (!of_node_cmp(to_of_node(child)->name, childname))
return child;
} else if (is_acpi_data_node(child)) {
if (acpi_data_node_match(child, childname))
return child;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(device_get_named_child_node);
/** /**
* fwnode_handle_put - Drop reference to a device node * fwnode_handle_put - Drop reference to a device node
* @fwnode: Pointer to the device node to drop the reference to. * @fwnode: Pointer to the device node to drop the reference to.
......
...@@ -420,6 +420,13 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn ...@@ -420,6 +420,13 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
container_of(fwnode, struct acpi_data_node, fwnode) : NULL; container_of(fwnode, struct acpi_data_node, fwnode) : NULL;
} }
static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
const char *name)
{
return is_acpi_data_node(fwnode) ?
(!strcmp(to_acpi_data_node(fwnode)->name, name)) : false;
}
static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
{ {
return &adev->fwnode; return &adev->fwnode;
......
...@@ -568,6 +568,12 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn ...@@ -568,6 +568,12 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
return NULL; return NULL;
} }
static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
const char *name)
{
return false;
}
static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
{ {
return NULL; return NULL;
......
...@@ -238,13 +238,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) ...@@ -238,13 +238,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1 #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
#endif #endif
/* Default string compare functions, Allow arch asm/prom.h to override */
#if !defined(of_compat_cmp)
#define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2))
#define of_prop_cmp(s1, s2) strcmp((s1), (s2))
#define of_node_cmp(s1, s2) strcasecmp((s1), (s2))
#endif
#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
...@@ -726,6 +719,13 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag ...@@ -726,6 +719,13 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
#define of_match_node(_matches, _node) NULL #define of_match_node(_matches, _node) NULL
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
/* Default string compare functions, Allow arch asm/prom.h to override */
#if !defined(of_compat_cmp)
#define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2))
#define of_prop_cmp(s1, s2) strcmp((s1), (s2))
#define of_node_cmp(s1, s2) strcasecmp((s1), (s2))
#endif
#if defined(CONFIG_OF) && defined(CONFIG_NUMA) #if defined(CONFIG_OF) && defined(CONFIG_NUMA)
extern int of_node_to_nid(struct device_node *np); extern int of_node_to_nid(struct device_node *np);
#else #else
......
...@@ -77,6 +77,9 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, ...@@ -77,6 +77,9 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
for (child = device_get_next_child_node(dev, NULL); child; \ for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child)) child = device_get_next_child_node(dev, child))
struct fwnode_handle *device_get_named_child_node(struct device *dev,
const char *childname);
void fwnode_handle_put(struct fwnode_handle *fwnode); void fwnode_handle_put(struct fwnode_handle *fwnode);
unsigned int device_get_child_node_count(struct device *dev); unsigned int device_get_child_node_count(struct device *dev);
......
...@@ -7,3 +7,12 @@ config SND_BCM2835_SOC_I2S ...@@ -7,3 +7,12 @@ config SND_BCM2835_SOC_I2S
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the BCM2835 I2S interface. You will also need the BCM2835 I2S interface. You will also need
to select the audio interfaces to support below. to select the audio interfaces to support below.
config SND_SOC_CYGNUS
tristate "SoC platform audio for Broadcom Cygnus chips"
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
help
Say Y if you want to add support for ASoC audio on Broadcom
Cygnus chips (bcm958300, bcm958305, bcm911360)
If you don't know what to do here, say N.
\ No newline at end of file
...@@ -3,3 +3,8 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o ...@@ -3,3 +3,8 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
# CYGNUS Platform Support
snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
/*
* Copyright (C) 2014-2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "cygnus-ssp.h"
/* Register offset needed for ASoC PCM module */
#define INTH_R5F_STATUS_OFFSET 0x040
#define INTH_R5F_CLEAR_OFFSET 0x048
#define INTH_R5F_MASK_SET_OFFSET 0x050
#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
#define BF_REARM_FREE_MARK_OFFSET 0x344
#define BF_REARM_FULL_MARK_OFFSET 0x348
/* Ring Buffer Ctrl Regs --- Start */
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
#define SRC_RBUF_0_RDADDR_OFFSET 0x500
#define SRC_RBUF_1_RDADDR_OFFSET 0x518
#define SRC_RBUF_2_RDADDR_OFFSET 0x530
#define SRC_RBUF_3_RDADDR_OFFSET 0x548
#define SRC_RBUF_4_RDADDR_OFFSET 0x560
#define SRC_RBUF_5_RDADDR_OFFSET 0x578
#define SRC_RBUF_6_RDADDR_OFFSET 0x590
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
#define SRC_RBUF_0_WRADDR_OFFSET 0x504
#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
#define SRC_RBUF_2_WRADDR_OFFSET 0x534
#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
#define SRC_RBUF_4_WRADDR_OFFSET 0x564
#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
#define SRC_RBUF_6_WRADDR_OFFSET 0x594
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
#define DST_RBUF_3_RDADDR_OFFSET 0x608
#define DST_RBUF_4_RDADDR_OFFSET 0x620
#define DST_RBUF_5_RDADDR_OFFSET 0x638
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
#define DST_RBUF_3_WRADDR_OFFSET 0x60c
#define DST_RBUF_4_WRADDR_OFFSET 0x624
#define DST_RBUF_5_WRADDR_OFFSET 0x63c
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
#define DST_RBUF_3_BASEADDR_OFFSET 0x610
#define DST_RBUF_4_BASEADDR_OFFSET 0x628
#define DST_RBUF_5_BASEADDR_OFFSET 0x640
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
#define DST_RBUF_3_ENDADDR_OFFSET 0x614
#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
#define DST_RBUF_5_ENDADDR_OFFSET 0x644
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
/* Ring Buffer Ctrl Regs --- End */
/* Error Status Regs --- Start */
/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
#define ESR0_STATUS_OFFSET 0x900
#define ESR1_STATUS_OFFSET 0x918
#define ESR2_STATUS_OFFSET 0x930
#define ESR3_STATUS_OFFSET 0x948
#define ESR4_STATUS_OFFSET 0x960
/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
#define ESR0_STATUS_CLR_OFFSET 0x908
#define ESR1_STATUS_CLR_OFFSET 0x920
#define ESR2_STATUS_CLR_OFFSET 0x938
#define ESR3_STATUS_CLR_OFFSET 0x950
#define ESR4_STATUS_CLR_OFFSET 0x968
/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
#define ESR0_MASK_STATUS_OFFSET 0x90c
#define ESR1_MASK_STATUS_OFFSET 0x924
#define ESR2_MASK_STATUS_OFFSET 0x93c
#define ESR3_MASK_STATUS_OFFSET 0x954
#define ESR4_MASK_STATUS_OFFSET 0x96c
/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
#define ESR0_MASK_SET_OFFSET 0x910
#define ESR1_MASK_SET_OFFSET 0x928
#define ESR2_MASK_SET_OFFSET 0x940
#define ESR3_MASK_SET_OFFSET 0x958
#define ESR4_MASK_SET_OFFSET 0x970
/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
#define ESR0_MASK_CLR_OFFSET 0x914
#define ESR1_MASK_CLR_OFFSET 0x92c
#define ESR2_MASK_CLR_OFFSET 0x944
#define ESR3_MASK_CLR_OFFSET 0x95c
#define ESR4_MASK_CLR_OFFSET 0x974
/* Error Status Regs --- End */
#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */
#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */
#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */
#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */
#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */
/* Mask for R5F register. Set all relevant interrupt for playback handler */
#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
BIT(R5F_ESR1_SHIFT) | \
BIT(R5F_ESR3_SHIFT))
/* Mask for R5F register. Set all relevant interrupt for capture handler */
#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
/*
* PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
* This number should be a multiple of 256. Minimum value is 256
*/
#define PERIOD_BYTES_MIN 0x100
static const struct snd_pcm_hardware cygnus_pcm_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
/* A period is basically an interrupt */
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = 0x10000,
/* period_min/max gives range of approx interrupts per buffer */
.periods_min = 2,
.periods_max = 8,
/*
* maximum buffer size in bytes = period_bytes_max * periods_max
* We allocate this amount of data for each enabled channel
*/
.buffer_bytes_max = 4 * 0x8000,
};
static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
static struct cygnus_aio_port *cygnus_dai_get_dma_data(
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
}
static void ringbuf_set_initial(void __iomem *audio_io,
struct ringbuf_regs *p_rbuf,
bool is_playback,
u32 start,
u32 periodsize,
u32 bufsize)
{
u32 initial_rd;
u32 initial_wr;
u32 end;
u32 fmark_val; /* free or full mark */
p_rbuf->period_bytes = periodsize;
p_rbuf->buf_size = bufsize;
if (is_playback) {
/* Set the pointers to indicate full (flip uppermost bit) */
initial_rd = start;
initial_wr = initial_rd ^ BIT(31);
} else {
/* Set the pointers to indicate empty */
initial_wr = start;
initial_rd = initial_wr;
}
end = start + bufsize - 1;
/*
* The interrupt will fire when free/full mark is *exceeded*
* The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
* to be PERIOD_BYTES_MIN less than the period size.
*/
fmark_val = periodsize - PERIOD_BYTES_MIN;
writel(start, audio_io + p_rbuf->baseaddr);
writel(end, audio_io + p_rbuf->endaddr);
writel(fmark_val, audio_io + p_rbuf->fmark);
writel(initial_rd, audio_io + p_rbuf->rdaddr);
writel(initial_wr, audio_io + p_rbuf->wraddr);
}
static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
struct ringbuf_regs *p_rbuf;
int status = 0;
aio = cygnus_dai_get_dma_data(substream);
/* Map the ssp portnum to a set of ring buffers. */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
p_rbuf = &aio->play_rb_regs;
switch (aio->portnum) {
case 0:
*p_rbuf = RINGBUF_REG_PLAYBACK(0);
break;
case 1:
*p_rbuf = RINGBUF_REG_PLAYBACK(2);
break;
case 2:
*p_rbuf = RINGBUF_REG_PLAYBACK(4);
break;
case 3: /* SPDIF */
*p_rbuf = RINGBUF_REG_PLAYBACK(6);
break;
default:
status = -EINVAL;
}
} else {
p_rbuf = &aio->capture_rb_regs;
switch (aio->portnum) {
case 0:
*p_rbuf = RINGBUF_REG_CAPTURE(0);
break;
case 1:
*p_rbuf = RINGBUF_REG_CAPTURE(2);
break;
case 2:
*p_rbuf = RINGBUF_REG_CAPTURE(4);
break;
default:
status = -EINVAL;
}
}
return status;
}
static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
struct ringbuf_regs *p_rbuf = NULL;
aio = cygnus_dai_get_dma_data(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
p_rbuf = &aio->play_rb_regs;
else
p_rbuf = &aio->capture_rb_regs;
return p_rbuf;
}
static void enable_intr(struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
u32 clear_mask;
aio = cygnus_dai_get_dma_data(substream);
/* The port number maps to the bit position to be cleared */
clear_mask = BIT(aio->portnum);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Clear interrupt status before enabling them */
writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
/* Unmask the interrupts of the given port*/
writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
writel(ANY_PLAYBACK_IRQ,
aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
} else {
writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
writel(ANY_CAPTURE_IRQ,
aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
}
}
static void disable_intr(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct cygnus_aio_port *aio;
u32 set_mask;
aio = cygnus_dai_get_dma_data(substream);
dev_dbg(rtd->cpu_dai->dev, "%s on port %d\n", __func__, aio->portnum);
/* The port number maps to the bit position to be set */
set_mask = BIT(aio->portnum);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Mask the interrupts of the given port*/
writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
} else {
writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
}
}
static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
enable_intr(substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
disable_intr(substream);
break;
default:
ret = -EINVAL;
}
return ret;
}
static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
struct ringbuf_regs *p_rbuf = NULL;
u32 regval;
aio = cygnus_dai_get_dma_data(substream);
p_rbuf = get_ringbuf(substream);
/*
* If free/full mark interrupt occurs, provide timestamp
* to ALSA and update appropriate idx by period_bytes
*/
snd_pcm_period_elapsed(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* Set the ring buffer to full */
regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
regval = regval ^ BIT(31);
writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
} else {
/* Set the ring buffer to empty */
regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
}
}
/*
* ESR0/1/3 status Description
* 0x1 I2S0_out port caused interrupt
* 0x2 I2S1_out port caused interrupt
* 0x4 I2S2_out port caused interrupt
* 0x8 SPDIF_out port caused interrupt
*/
static void handle_playback_irq(struct cygnus_audio *cygaud)
{
void __iomem *audio_io;
u32 port;
u32 esr_status0, esr_status1, esr_status3;
audio_io = cygaud->audio;
/*
* ESR status gets updates with/without interrupts enabled.
* So, check the ESR mask, which provides interrupt enable/
* disable status and use it to determine which ESR status
* should be serviced.
*/
esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
u32 esrmask = BIT(port);
/*
* Ringbuffer or FIFO underflow
* If we get this interrupt then, it is also true that we have
* not yet responded to the freemark interrupt.
* Log a debug message. The freemark handler below will
* handle getting everything going again.
*/
if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
dev_dbg(cygaud->dev,
"Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
esr_status0, esr_status1, esr_status3);
}
/*
* Freemark is hit. This is the normal interrupt.
* In typical operation the read and write regs will be equal
*/
if (esrmask & esr_status3) {
struct snd_pcm_substream *playstr;
playstr = cygaud->portinfo[port].play_stream;
cygnus_pcm_period_elapsed(playstr);
}
}
/* Clear ESR interrupt */
writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
/* Rearm freemark logic by writing 1 to the correct bit */
writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
}
/*
* ESR2/4 status Description
* 0x1 I2S0_in port caused interrupt
* 0x2 I2S1_in port caused interrupt
* 0x4 I2S2_in port caused interrupt
*/
static void handle_capture_irq(struct cygnus_audio *cygaud)
{
void __iomem *audio_io;
u32 port;
u32 esr_status2, esr_status4;
audio_io = cygaud->audio;
/*
* ESR status gets updates with/without interrupts enabled.
* So, check the ESR mask, which provides interrupt enable/
* disable status and use it to determine which ESR status
* should be serviced.
*/
esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
u32 esrmask = BIT(port);
/*
* Ringbuffer or FIFO overflow
* If we get this interrupt then, it is also true that we have
* not yet responded to the fullmark interrupt.
* Log a debug message. The fullmark handler below will
* handle getting everything going again.
*/
if (esrmask & esr_status2)
dev_dbg(cygaud->dev,
"Overflow: esr2=0x%x\n", esr_status2);
if (esrmask & esr_status4) {
struct snd_pcm_substream *capstr;
capstr = cygaud->portinfo[port].capture_stream;
cygnus_pcm_period_elapsed(capstr);
}
}
writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
/* Rearm fullmark logic by writing 1 to the correct bit */
writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
}
static irqreturn_t cygnus_dma_irq(int irq, void *data)
{
u32 r5_status;
struct cygnus_audio *cygaud = data;
/*
* R5 status bits Description
* 0 ESR0 (playback FIFO interrupt)
* 1 ESR1 (playback rbuf interrupt)
* 2 ESR2 (capture rbuf interrupt)
* 3 ESR3 (Freemark play. interrupt)
* 4 ESR4 (Fullmark capt. interrupt)
*/
r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
return IRQ_NONE;
/* If playback interrupt happened */
if (ANY_PLAYBACK_IRQ & r5_status) {
handle_playback_irq(cygaud);
writel(ANY_PLAYBACK_IRQ & r5_status,
cygaud->audio + INTH_R5F_CLEAR_OFFSET);
}
/* If capture interrupt happened */
if (ANY_CAPTURE_IRQ & r5_status) {
handle_capture_irq(cygaud);
writel(ANY_CAPTURE_IRQ & r5_status,
cygaud->audio + INTH_R5F_CLEAR_OFFSET);
}
return IRQ_HANDLED;
}
static int cygnus_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct cygnus_aio_port *aio;
int ret;
aio = cygnus_dai_get_dma_data(substream);
if (!aio)
return -ENODEV;
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
if (ret < 0)
return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
if (ret < 0)
return ret;
/*
* Keep track of which substream belongs to which port.
* This info is needed by snd_pcm_period_elapsed() in irq_handler
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->play_stream = substream;
else
aio->capture_stream = substream;
return 0;
}
static int cygnus_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct cygnus_aio_port *aio;
aio = cygnus_dai_get_dma_data(substream);
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->play_stream = NULL;
else
aio->capture_stream = NULL;
if (!aio->play_stream && !aio->capture_stream)
dev_dbg(rtd->cpu_dai->dev, "freed port %d\n", aio->portnum);
return 0;
}
static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct cygnus_aio_port *aio;
int ret = 0;
aio = cygnus_dai_get_dma_data(substream);
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
return ret;
}
static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct cygnus_aio_port *aio;
aio = cygnus_dai_get_dma_data(substream);
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct cygnus_aio_port *aio;
unsigned long bufsize, periodsize;
int ret = 0;
bool is_play;
u32 start;
struct ringbuf_regs *p_rbuf = NULL;
aio = cygnus_dai_get_dma_data(substream);
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
bufsize = snd_pcm_lib_buffer_bytes(substream);
periodsize = snd_pcm_lib_period_bytes(substream);
dev_dbg(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n",
__func__, bufsize, periodsize);
configure_ringbuf_regs(substream);
p_rbuf = get_ringbuf(substream);
start = runtime->dma_addr;
is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
periodsize, bufsize);
return ret;
}
static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
{
struct cygnus_aio_port *aio;
unsigned int res = 0, cur = 0, base = 0;
struct ringbuf_regs *p_rbuf = NULL;
aio = cygnus_dai_get_dma_data(substream);
/*
* Get the offset of the current read (for playack) or write
* index (for capture). Report this value back to the asoc framework.
*/
p_rbuf = get_ringbuf(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
else
cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
/*
* Mask off the MSB of the rdaddr,wraddr and baseaddr
* since MSB is not part of the address
*/
res = (cur & 0x7fffffff) - (base & 0x7fffffff);
return bytes_to_frames(substream->runtime, res);
}
static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size;
size = cygnus_pcm_hw.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
__func__, size, buf->area);
if (!buf->area) {
dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
return -ENOMEM;
}
buf->bytes = size;
return 0;
}
static const struct snd_pcm_ops cygnus_pcm_ops = {
.open = cygnus_pcm_open,
.close = cygnus_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = cygnus_pcm_hw_params,
.hw_free = cygnus_pcm_hw_free,
.prepare = cygnus_pcm_prepare,
.trigger = cygnus_pcm_trigger,
.pointer = cygnus_pcm_pointer,
};
static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
if (substream) {
buf = &substream->dma_buffer;
if (buf->area) {
dma_free_coherent(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
}
}
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (substream) {
buf = &substream->dma_buffer;
if (buf->area) {
dma_free_coherent(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
}
}
}
static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret;
if (!card->dev->dma_mask)
card->dev->dma_mask = &cygnus_dma_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = cygnus_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
return ret;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = cygnus_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret) {
cygnus_dma_free_dma_buffers(pcm);
return ret;
}
}
return 0;
}
static struct snd_soc_platform_driver cygnus_soc_platform = {
.ops = &cygnus_pcm_ops,
.pcm_new = cygnus_dma_new,
.pcm_free = cygnus_dma_free_dma_buffers,
};
int cygnus_soc_platform_register(struct device *dev,
struct cygnus_audio *cygaud)
{
int rc = 0;
dev_dbg(dev, "%s Enter\n", __func__);
rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
IRQF_SHARED, "cygnus-audio", cygaud);
if (rc) {
dev_err(dev, "%s request_irq error %d\n", __func__, rc);
return rc;
}
rc = snd_soc_register_platform(dev, &cygnus_soc_platform);
if (rc) {
dev_err(dev, "%s failed\n", __func__);
return rc;
}
return 0;
}
int cygnus_soc_platform_unregister(struct device *dev)
{
snd_soc_unregister_platform(dev);
return 0;
}
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("Cygnus ASoC PCM module");
/*
* Copyright (C) 2014-2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "cygnus-ssp.h"
#define DEFAULT_VCO 1354750204
#define CYGNUS_TDM_RATE \
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
#define CAPTURE_FCI_ID_BASE 0x180
#define CYGNUS_SSP_TRISTATE_MASK 0x001fff
#define CYGNUS_PLLCLKSEL_MASK 0xf
/* Used with stream_on field to indicate which streams are active */
#define PLAYBACK_STREAM_MASK BIT(0)
#define CAPTURE_STREAM_MASK BIT(1)
#define I2S_STREAM_CFG_MASK 0xff003ff
#define I2S_CAP_STREAM_CFG_MASK 0xf0
#define SPDIF_STREAM_CFG_MASK 0x3ff
#define CH_GRP_STEREO 0x1
/* Begin register offset defines */
#define AUD_MISC_SEROUT_OE_REG_BASE 0x01c
#define AUD_MISC_SEROUT_SPDIF_OE 12
#define AUD_MISC_SEROUT_MCLK_OE 3
#define AUD_MISC_SEROUT_LRCK_OE 2
#define AUD_MISC_SEROUT_SCLK_OE 1
#define AUD_MISC_SEROUT_SDAT_OE 0
/* AUD_FMM_BF_CTRL_xxx regs */
#define BF_DST_CFG0_OFFSET 0x100
#define BF_DST_CFG1_OFFSET 0x104
#define BF_DST_CFG2_OFFSET 0x108
#define BF_DST_CTRL0_OFFSET 0x130
#define BF_DST_CTRL1_OFFSET 0x134
#define BF_DST_CTRL2_OFFSET 0x138
#define BF_SRC_CFG0_OFFSET 0x148
#define BF_SRC_CFG1_OFFSET 0x14c
#define BF_SRC_CFG2_OFFSET 0x150
#define BF_SRC_CFG3_OFFSET 0x154
#define BF_SRC_CTRL0_OFFSET 0x1c0
#define BF_SRC_CTRL1_OFFSET 0x1c4
#define BF_SRC_CTRL2_OFFSET 0x1c8
#define BF_SRC_CTRL3_OFFSET 0x1cc
#define BF_SRC_GRP0_OFFSET 0x1fc
#define BF_SRC_GRP1_OFFSET 0x200
#define BF_SRC_GRP2_OFFSET 0x204
#define BF_SRC_GRP3_OFFSET 0x208
#define BF_SRC_GRP_EN_OFFSET 0x320
#define BF_SRC_GRP_FLOWON_OFFSET 0x324
#define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328
/* AUD_FMM_IOP_OUT_I2S_xxx regs */
#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
#define OUT_I2S_0_CFG_OFFSET 0xa04
#define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c
#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
#define OUT_I2S_1_CFG_OFFSET 0xa44
#define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c
#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
#define OUT_I2S_2_CFG_OFFSET 0xa84
#define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c
/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
#define SPDIF_STREAM_CFG_OFFSET 0xac0
#define SPDIF_CTRL_OFFSET 0xac4
#define SPDIF_FORMAT_CFG_OFFSET 0xad8
#define SPDIF_MCLK_CFG_OFFSET 0xadc
/* AUD_FMM_IOP_PLL_0_xxx regs */
#define IOP_PLL_0_MACRO_OFFSET 0xb00
#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
/* AUD_FMM_IOP_xxx regs */
#define IOP_PLL_0_CONTROL_OFFSET 0xb04
#define IOP_PLL_0_USER_NDIV_OFFSET 0xb08
#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
#define IOP_PLL_0_RESET_OFFSET 0xb5c
/* AUD_FMM_IOP_IN_I2S_xxx regs */
#define IN_I2S_0_STREAM_CFG_OFFSET 0x00
#define IN_I2S_0_CFG_OFFSET 0x04
#define IN_I2S_1_STREAM_CFG_OFFSET 0x40
#define IN_I2S_1_CFG_OFFSET 0x44
#define IN_I2S_2_STREAM_CFG_OFFSET 0x80
#define IN_I2S_2_CFG_OFFSET 0x84
/* AUD_FMM_IOP_MISC_xxx regs */
#define IOP_SW_INIT_LOGIC 0x1c0
/* End register offset defines */
/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
#define I2S_OUT_MCLKRATE_SHIFT 16
/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
#define I2S_OUT_PLLCLKSEL_SHIFT 0
/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
#define I2S_OUT_STREAM_ENA 31
#define I2S_OUT_STREAM_CFG_GROUP_ID 20
#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24
/* AUD_FMM_IOP_IN_I2S_x_CAP */
#define I2S_IN_STREAM_CFG_CAP_ENA 31
#define I2S_IN_STREAM_CFG_0_GROUP_ID 4
/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
#define I2S_OUT_CFGX_CLK_ENA 0
#define I2S_OUT_CFGX_DATA_ENABLE 1
#define I2S_OUT_CFGX_DATA_ALIGNMENT 6
#define I2S_OUT_CFGX_BITS_PER_SLOT 13
#define I2S_OUT_CFGX_VALID_SLOT 14
#define I2S_OUT_CFGX_FSYNC_WIDTH 18
#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
#define I2S_OUT_CFGX_SLAVE_MODE 30
#define I2S_OUT_CFGX_TDM_MODE 31
/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
#define BF_SRC_CFGX_SFIFO_ENA 0
#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1
#define BF_SRC_CFGX_SAMPLE_CH_MODE 2
#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5
#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10
#define BF_SRC_CFGX_BIT_RES 20
#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31
/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
#define BF_DST_CFGX_CAP_ENA 0
#define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1
#define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2
#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
#define BF_DST_CFGX_FCI_ID 12
#define BF_DST_CFGX_CAP_MODE 24
#define BF_DST_CFGX_PROC_SEQ_ID_VALID 31
/* AUD_FMM_IOP_OUT_SPDIF_xxx */
#define SPDIF_0_OUT_DITHER_ENA 3
#define SPDIF_0_OUT_STREAM_ENA 31
/* AUD_FMM_IOP_PLL_0_USER */
#define IOP_PLL_0_USER_NDIV_FRAC 10
/* AUD_FMM_IOP_PLL_0_ACTIVE */
#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
.i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
.i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
.i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
.i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
.i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
.bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
.bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
.bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
.bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
.bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
}
struct pll_macro_entry {
u32 mclk;
u32 pll_ch_num;
};
/*
* PLL has 3 output channels (1x, 2x, and 4x). Below are
* the common MCLK frequencies used by audio driver
*/
static const struct pll_macro_entry pll_predef_mclk[] = {
{ 4096000, 0},
{ 8192000, 1},
{16384000, 2},
{ 5644800, 0},
{11289600, 1},
{22579200, 2},
{ 6144000, 0},
{12288000, 1},
{24576000, 2},
{12288000, 0},
{24576000, 1},
{49152000, 2},
{22579200, 0},
{45158400, 1},
{90316800, 2},
{24576000, 0},
{49152000, 1},
{98304000, 2},
};
/* List of valid frame sizes for tdm mode */
static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
/*
* Use this relationship to derive the sampling rate (lrclk)
* lrclk = (mclk) / ((2*mclk_to_sclk_ratio) * (32 * SCLK))).
*
* Use mclk and pll_ch from the table above
*
* Valid SCLK = 0/1/2/4/8/12
*
* mclk_to_sclk_ratio = number of MCLK per SCLK. Division is twice the
* value programmed in this field.
* Valid mclk_to_sclk_ratio = 1 through to 15
*
* eg: To set lrclk = 48khz, set mclk = 12288000, mclk_to_sclk_ratio = 2,
* SCLK = 64
*/
struct _ssp_clk_coeff {
u32 mclk;
u32 sclk_rate;
u32 rate;
u32 mclk_rate;
};
static const struct _ssp_clk_coeff ssp_clk_coeff[] = {
{ 4096000, 32, 16000, 4},
{ 4096000, 32, 32000, 2},
{ 4096000, 64, 8000, 4},
{ 4096000, 64, 16000, 2},
{ 4096000, 64, 32000, 1},
{ 4096000, 128, 8000, 2},
{ 4096000, 128, 16000, 1},
{ 4096000, 256, 8000, 1},
{ 6144000, 32, 16000, 6},
{ 6144000, 32, 32000, 3},
{ 6144000, 32, 48000, 2},
{ 6144000, 32, 96000, 1},
{ 6144000, 64, 8000, 6},
{ 6144000, 64, 16000, 3},
{ 6144000, 64, 48000, 1},
{ 6144000, 128, 8000, 3},
{ 8192000, 32, 32000, 4},
{ 8192000, 64, 16000, 4},
{ 8192000, 64, 32000, 2},
{ 8192000, 128, 8000, 4},
{ 8192000, 128, 16000, 2},
{ 8192000, 128, 32000, 1},
{ 8192000, 256, 8000, 2},
{ 8192000, 256, 16000, 1},
{ 8192000, 512, 8000, 1},
{12288000, 32, 32000, 6},
{12288000, 32, 48000, 4},
{12288000, 32, 96000, 2},
{12288000, 32, 192000, 1},
{12288000, 64, 16000, 6},
{12288000, 64, 32000, 3},
{12288000, 64, 48000, 2},
{12288000, 64, 96000, 1},
{12288000, 128, 8000, 6},
{12288000, 128, 16000, 3},
{12288000, 128, 48000, 1},
{12288000, 256, 8000, 3},
{16384000, 64, 32000, 4},
{16384000, 128, 16000, 4},
{16384000, 128, 32000, 2},
{16384000, 256, 8000, 4},
{16384000, 256, 16000, 2},
{16384000, 256, 32000, 1},
{16384000, 512, 8000, 2},
{16384000, 512, 16000, 1},
{24576000, 32, 96000, 4},
{24576000, 32, 192000, 2},
{24576000, 64, 32000, 6},
{24576000, 64, 48000, 4},
{24576000, 64, 96000, 2},
{24576000, 64, 192000, 1},
{24576000, 128, 16000, 6},
{24576000, 128, 32000, 3},
{24576000, 128, 48000, 2},
{24576000, 256, 8000, 6},
{24576000, 256, 16000, 3},
{24576000, 256, 48000, 1},
{24576000, 512, 8000, 3},
{49152000, 32, 192000, 4},
{49152000, 64, 96000, 4},
{49152000, 64, 192000, 2},
{49152000, 128, 32000, 6},
{49152000, 128, 48000, 4},
{49152000, 128, 96000, 2},
{49152000, 128, 192000, 1},
{49152000, 256, 16000, 6},
{49152000, 256, 32000, 3},
{49152000, 256, 48000, 2},
{49152000, 256, 96000, 1},
{49152000, 512, 8000, 6},
{49152000, 512, 16000, 3},
{49152000, 512, 48000, 1},
{ 5644800, 32, 22050, 4},
{ 5644800, 32, 44100, 2},
{ 5644800, 32, 88200, 1},
{ 5644800, 64, 11025, 4},
{ 5644800, 64, 22050, 2},
{ 5644800, 64, 44100, 1},
{11289600, 32, 44100, 4},
{11289600, 32, 88200, 2},
{11289600, 32, 176400, 1},
{11289600, 64, 22050, 4},
{11289600, 64, 44100, 2},
{11289600, 64, 88200, 1},
{11289600, 128, 11025, 4},
{11289600, 128, 22050, 2},
{11289600, 128, 44100, 1},
{22579200, 32, 88200, 4},
{22579200, 32, 176400, 2},
{22579200, 64, 44100, 4},
{22579200, 64, 88200, 2},
{22579200, 64, 176400, 1},
{22579200, 128, 22050, 4},
{22579200, 128, 44100, 2},
{22579200, 128, 88200, 1},
{22579200, 256, 11025, 4},
{22579200, 256, 22050, 2},
{22579200, 256, 44100, 1},
{45158400, 32, 176400, 4},
{45158400, 64, 88200, 4},
{45158400, 64, 176400, 2},
{45158400, 128, 44100, 4},
{45158400, 128, 88200, 2},
{45158400, 128, 176400, 1},
{45158400, 256, 22050, 4},
{45158400, 256, 44100, 2},
{45158400, 256, 88200, 1},
{45158400, 512, 11025, 4},
{45158400, 512, 22050, 2},
{45158400, 512, 44100, 1},
};
static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
{
struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
return &cygaud->portinfo[dai->id];
}
static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
{
u32 value, fci_id;
int status = 0;
switch (aio->port_type) {
case PORT_TDM:
value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
value &= ~I2S_STREAM_CFG_MASK;
/* Set Group ID */
writel(aio->portnum,
aio->cygaud->audio + aio->regs.bf_sourcech_grp);
/* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
value |= aio->portnum; /* FCI ID is the port num */
value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
/* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
/* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
value = readl(aio->cygaud->i2s_in +
aio->regs.i2s_cap_stream_cfg);
value &= ~I2S_CAP_STREAM_CFG_MASK;
value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
writel(value, aio->cygaud->i2s_in +
aio->regs.i2s_cap_stream_cfg);
/* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
value |= (fci_id << BF_DST_CFGX_FCI_ID);
value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
/* Enable the transmit pin for this port */
value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
break;
case PORT_SPDIF:
writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
value |= BIT(SPDIF_0_OUT_DITHER_ENA);
writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
/* Enable and set the FCI ID for the SPDIF channel */
value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
value &= ~SPDIF_STREAM_CFG_MASK;
value |= aio->portnum; /* FCI ID is the port num */
value |= BIT(SPDIF_0_OUT_STREAM_ENA);
writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
/* Enable the spdif output pin */
value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
break;
default:
dev_err(aio->cygaud->dev, "Port not supported\n");
status = -EINVAL;
}
return status;
}
static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
{
u32 value;
value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
value |= BIT(BF_DST_CFGX_CAP_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value |= BIT(I2S_OUT_CFGX_CLK_ENA);
value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
aio->streams_on |= CAPTURE_STREAM_MASK;
}
static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
{
u32 value;
value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
aio->streams_on &= ~CAPTURE_STREAM_MASK;
/* If both playback and capture are off */
if (!aio->streams_on) {
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
}
writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
value &= ~BIT(BF_DST_CFGX_CAP_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
}
static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
{
u32 value;
int status = 0;
switch (aio->port_type) {
case PORT_TDM:
value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
value |= BIT(I2S_OUT_STREAM_ENA);
writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value |= BIT(I2S_OUT_CFGX_CLK_ENA);
value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
aio->streams_on |= PLAYBACK_STREAM_MASK;
break;
case PORT_SPDIF:
value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
value |= 0x3;
writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
break;
default:
dev_err(aio->cygaud->dev,
"Port not supported %d\n", aio->portnum);
status = -EINVAL;
}
return status;
}
static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
{
u32 value;
int status = 0;
switch (aio->port_type) {
case PORT_TDM:
aio->streams_on &= ~PLAYBACK_STREAM_MASK;
/* If both playback and capture are off */
if (!aio->streams_on) {
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
}
/* set group_sync_dis = 1 */
value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
value |= BIT(aio->portnum);
writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
/* set group_sync_dis = 0 */
value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
value &= ~BIT(aio->portnum);
writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
value &= ~BIT(I2S_OUT_STREAM_ENA);
writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
/* IOP SW INIT on OUT_I2S_x */
value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
value |= BIT(aio->portnum);
writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
value &= ~BIT(aio->portnum);
writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
break;
case PORT_SPDIF:
value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
value &= ~0x3;
writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
break;
default:
dev_err(aio->cygaud->dev,
"Port not supported %d\n", aio->portnum);
status = -EINVAL;
}
return status;
}
static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
struct cygnus_aio_port *aio)
{
int i = 0, error;
bool found = false;
const struct pll_macro_entry *p_entry;
struct clk *ch_clk;
for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
p_entry = &pll_predef_mclk[i];
if (p_entry->mclk == mclk) {
found = true;
break;
}
}
if (!found) {
dev_err(cygaud->dev,
"%s No valid mclk freq (%u) found!\n", __func__, mclk);
return -EINVAL;
}
ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
error = clk_prepare_enable(ch_clk);
if (error) {
dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
__func__, error);
return error;
}
aio->clk_trace.cap_clk_en = true;
}
if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
error = clk_prepare_enable(ch_clk);
if (error) {
dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
__func__, error);
return error;
}
aio->clk_trace.play_clk_en = true;
}
error = clk_set_rate(ch_clk, mclk);
if (error) {
dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
__func__, error);
return error;
}
return p_entry->pll_ch_num;
}
static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio,
struct cygnus_audio *cygaud)
{
u32 value, i = 0;
u32 mask = 0xf;
u32 sclk;
bool found = false;
const struct _ssp_clk_coeff *p_entry = NULL;
for (i = 0; i < ARRAY_SIZE(ssp_clk_coeff); i++) {
p_entry = &ssp_clk_coeff[i];
if ((p_entry->rate == aio->lrclk) &&
(p_entry->sclk_rate == aio->bit_per_frame) &&
(p_entry->mclk == aio->mclk)) {
found = true;
break;
}
}
if (!found) {
dev_err(aio->cygaud->dev,
"No valid match found in ssp_clk_coeff array\n");
dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
aio->lrclk, aio->bit_per_frame, aio->mclk);
return -EINVAL;
}
sclk = aio->bit_per_frame;
if (sclk == 512)
sclk = 0;
/* sclks_per_1fs_div = sclk cycles/32 */
sclk /= 32;
/* Set sclk rate */
switch (aio->port_type) {
case PORT_TDM:
/* Set number of bitclks per frame */
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
dev_dbg(aio->cygaud->dev,
"SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
break;
case PORT_SPDIF:
break;
default:
dev_err(aio->cygaud->dev, "Unknown port type\n");
return -EINVAL;
}
/* Set MCLK_RATE ssp port (spdif and ssp are the same) */
value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
value |= (p_entry->mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
aio->bit_per_frame, aio->mclk, aio->lrclk);
return 0;
}
static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
int rate, bitres;
u32 value;
u32 mask = 0x1f;
int ret = 0;
dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
dev_dbg(aio->cygaud->dev, "params_channels %d\n",
params_channels(params));
dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
rate = params_rate(params);
switch (aio->mode) {
case CYGNUS_SSPMODE_TDM:
if ((rate == 192000) && (params_channels(params) > 4)) {
dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
params_channels(params), rate);
return -EINVAL;
}
break;
case CYGNUS_SSPMODE_I2S:
aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
break;
default:
dev_err(aio->cygaud->dev,
"%s port running in unknown mode\n", __func__);
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
/* Configure channels as mono or stereo/TDM */
if (params_channels(params) == 1)
value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
else
value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
if (aio->port_type == PORT_SPDIF) {
dev_err(aio->cygaud->dev,
"SPDIF does not support 8bit format\n");
return -EINVAL;
}
bitres = 8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
bitres = 16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
/* 32 bit mode is coded as 0 */
bitres = 0;
break;
default:
return -EINVAL;
}
value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
value &= ~(mask << BF_SRC_CFGX_BIT_RES);
value |= (bitres << BF_SRC_CFGX_BIT_RES);
writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
} else {
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
value = readl(aio->cygaud->audio +
aio->regs.bf_destch_cfg);
value |= BIT(BF_DST_CFGX_CAP_MODE);
writel(value, aio->cygaud->audio +
aio->regs.bf_destch_cfg);
break;
case SNDRV_PCM_FORMAT_S32_LE:
value = readl(aio->cygaud->audio +
aio->regs.bf_destch_cfg);
value &= ~BIT(BF_DST_CFGX_CAP_MODE);
writel(value, aio->cygaud->audio +
aio->regs.bf_destch_cfg);
break;
default:
return -EINVAL;
}
}
aio->lrclk = rate;
if (!aio->is_slave)
ret = cygnus_ssp_set_clocks(aio, cygaud);
return ret;
}
/*
* This function sets the mclk frequency for pll clock
*/
static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
int sel;
u32 value;
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
dev_dbg(aio->cygaud->dev,
"%s Enter port = %d\n", __func__, aio->portnum);
sel = pll_configure_mclk(cygaud, freq, aio);
if (sel < 0) {
dev_err(aio->cygaud->dev,
"%s Setting mclk failed.\n", __func__);
return -EINVAL;
}
aio->mclk = freq;
dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
return 0;
}
static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
snd_soc_dai_set_dma_data(dai, substream, aio);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->clk_trace.play_en = true;
else
aio->clk_trace.cap_en = true;
return 0;
}
static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aio->clk_trace.play_en = false;
else
aio->clk_trace.cap_en = false;
if (!aio->is_slave) {
u32 val;
val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
val &= CYGNUS_PLLCLKSEL_MASK;
if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
val);
return;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (aio->clk_trace.play_clk_en) {
clk_disable_unprepare(aio->cygaud->
audio_clk[val]);
aio->clk_trace.play_clk_en = false;
}
} else {
if (aio->clk_trace.cap_clk_en) {
clk_disable_unprepare(aio->cygaud->
audio_clk[val]);
aio->clk_trace.cap_clk_en = false;
}
}
}
}
/*
* Bit Update Notes
* 31 Yes TDM Mode (1 = TDM, 0 = i2s)
* 30 Yes Slave Mode (1 = Slave, 0 = Master)
* 29:26 No Sclks per frame
* 25:18 Yes FS Width
* 17:14 No Valid Slots
* 13 No Bits (1 = 16 bits, 0 = 32 bits)
* 12:08 No Bits per samp
* 07 Yes Justifcation (1 = LSB, 0 = MSB)
* 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay
* 05 Yes SCLK polarity (1 = Rising, 0 = Falling)
* 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left)
* 03:02 Yes Reserved - write as zero
* 01 No Data Enable
* 00 No CLK Enable
*/
#define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03
/* Input cfg is same as output, but the FS width is not a valid field */
#define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
if ((len > 0) && (len < 256)) {
aio->fsync_width = len;
return 0;
} else {
return -EINVAL;
}
}
static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
u32 ssp_curcfg;
u32 ssp_newcfg;
u32 ssp_outcfg;
u32 ssp_incfg;
u32 val;
u32 mask;
dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt);
if (aio->port_type == PORT_SPDIF)
return -EINVAL;
ssp_newcfg = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
aio->is_slave = 0;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
aio->mode = CYGNUS_SSPMODE_I2S;
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
/* DSP_A = data after FS, DSP_B = data during FS */
if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
ssp_newcfg |=
(aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
else
ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
aio->mode = CYGNUS_SSPMODE_TDM;
break;
default:
return -EINVAL;
}
/*
* SSP out cfg.
* Retain bits we do not want to update, then OR in new bits
*/
ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
/*
* SSP in cfg.
* Retain bits we do not want to update, then OR in new bits
*/
ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
/*
* Configure the word clk and bit clk as output or tristate
* Each port has 4 bits for controlling its pins.
* Shift the mask based upon port number.
*/
mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
| BIT(AUD_MISC_SEROUT_SCLK_OE)
| BIT(AUD_MISC_SEROUT_MCLK_OE);
mask = mask << (aio->portnum * 4);
if (aio->is_slave)
/* Set bit for tri-state */
val |= mask;
else
/* Clear bit for drive */
val &= ~mask;
dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val);
writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
return 0;
}
static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
dev_dbg(aio->cygaud->dev,
"%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_ssp_out_enable(aio);
else
audio_ssp_in_enable(aio);
cygaud->active_ports++;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_ssp_out_disable(aio);
else
audio_ssp_in_disable(aio);
cygaud->active_ports--;
break;
default:
return -EINVAL;
}
return 0;
}
static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
u32 value;
int bits_per_slot = 0; /* default to 32-bits per slot */
int frame_bits;
unsigned int active_slots;
bool found = false;
int i;
if (tx_mask != rx_mask) {
dev_err(aio->cygaud->dev,
"%s tx_mask must equal rx_mask\n", __func__);
return -EINVAL;
}
active_slots = hweight32(tx_mask);
if ((active_slots < 0) || (active_slots > 16))
return -EINVAL;
/* Slot value must be even */
if (active_slots % 2)
return -EINVAL;
/* We encode 16 slots as 0 in the reg */
if (active_slots == 16)
active_slots = 0;
/* Slot Width is either 16 or 32 */
switch (slot_width) {
case 16:
bits_per_slot = 1;
break;
case 32:
bits_per_slot = 0;
break;
default:
bits_per_slot = 0;
dev_warn(aio->cygaud->dev,
"%s Defaulting Slot Width to 32\n", __func__);
}
frame_bits = slots * slot_width;
for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
if (ssp_valid_tdm_framesize[i] == frame_bits) {
found = true;
break;
}
}
if (!found) {
dev_err(aio->cygaud->dev,
"%s In TDM mode, frame bits INVALID (%d)\n",
__func__, frame_bits);
return -EINVAL;
}
aio->bit_per_frame = frame_bits;
dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
__func__, active_slots, frame_bits);
/* Set capture side of ssp port */
value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
/* Set playback side of ssp port */
value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
if (!aio->is_slave) {
u32 val;
val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
val &= CYGNUS_PLLCLKSEL_MASK;
if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
val);
return -EINVAL;
}
if (aio->clk_trace.cap_clk_en)
clk_disable_unprepare(aio->cygaud->audio_clk[val]);
if (aio->clk_trace.play_clk_en)
clk_disable_unprepare(aio->cygaud->audio_clk[val]);
aio->pll_clk_num = val;
}
return 0;
}
static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
{
struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
int error;
if (!aio->is_slave) {
if (aio->clk_trace.cap_clk_en) {
error = clk_prepare_enable(aio->cygaud->
audio_clk[aio->pll_clk_num]);
if (error) {
dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
__func__);
return -EINVAL;
}
}
if (aio->clk_trace.play_clk_en) {
error = clk_prepare_enable(aio->cygaud->
audio_clk[aio->pll_clk_num]);
if (error) {
if (aio->clk_trace.cap_clk_en)
clk_disable_unprepare(aio->cygaud->
audio_clk[aio->pll_clk_num]);
dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
__func__);
return -EINVAL;
}
}
}
return 0;
}
#else
#define cygnus_ssp_suspend NULL
#define cygnus_ssp_resume NULL
#endif
static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
.startup = cygnus_ssp_startup,
.shutdown = cygnus_ssp_shutdown,
.trigger = cygnus_ssp_trigger,
.hw_params = cygnus_ssp_hw_params,
.set_fmt = cygnus_ssp_set_fmt,
.set_sysclk = cygnus_ssp_set_sysclk,
.set_tdm_slot = cygnus_set_dai_tdm_slot,
};
#define INIT_CPU_DAI(num) { \
.name = "cygnus-ssp" #num, \
.playback = { \
.channels_min = 1, \
.channels_max = 16, \
.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.channels_min = 2, \
.channels_max = 16, \
.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &cygnus_ssp_dai_ops, \
.suspend = cygnus_ssp_suspend, \
.resume = cygnus_ssp_resume, \
}
static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
INIT_CPU_DAI(0),
INIT_CPU_DAI(1),
INIT_CPU_DAI(2),
};
static struct snd_soc_dai_driver cygnus_spdif_dai_info = {
.name = "cygnus-spdif",
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &cygnus_ssp_dai_ops,
.suspend = cygnus_ssp_suspend,
.resume = cygnus_ssp_resume,
};
static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
static const struct snd_soc_component_driver cygnus_ssp_component = {
.name = "cygnus-audio",
};
/*
* Return < 0 if error
* Return 0 if disabled
* Return 1 if enabled and node is parsed successfully
*/
static int parse_ssp_child_node(struct platform_device *pdev,
struct device_node *dn,
struct cygnus_audio *cygaud,
struct snd_soc_dai_driver *p_dai)
{
struct cygnus_aio_port *aio;
struct cygnus_ssp_regs ssp_regs[3];
u32 rawval;
int portnum = -1;
enum cygnus_audio_port_type port_type;
if (of_property_read_u32(dn, "reg", &rawval)) {
dev_err(&pdev->dev, "Missing reg property\n");
return -EINVAL;
}
portnum = rawval;
switch (rawval) {
case 0:
ssp_regs[0] = INIT_SSP_REGS(0);
port_type = PORT_TDM;
break;
case 1:
ssp_regs[1] = INIT_SSP_REGS(1);
port_type = PORT_TDM;
break;
case 2:
ssp_regs[2] = INIT_SSP_REGS(2);
port_type = PORT_TDM;
break;
case 3:
port_type = PORT_SPDIF;
break;
default:
dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
return -EINVAL;
}
aio = &cygaud->portinfo[portnum];
aio->cygaud = cygaud;
aio->portnum = portnum;
aio->port_type = port_type;
aio->fsync_width = -1;
switch (port_type) {
case PORT_TDM:
aio->regs = ssp_regs[portnum];
*p_dai = cygnus_ssp_dai_info[portnum];
aio->mode = CYGNUS_SSPMODE_UNKNOWN;
break;
case PORT_SPDIF:
aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
*p_dai = cygnus_spdif_dai_info;
/* For the purposes of this code SPDIF can be I2S mode */
aio->mode = CYGNUS_SSPMODE_I2S;
break;
default:
dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
return -EINVAL;
}
dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
aio->streams_on = 0;
aio->cygaud->dev = &pdev->dev;
aio->clk_trace.play_en = false;
aio->clk_trace.cap_en = false;
audio_ssp_init_portregs(aio);
return 0;
}
static int audio_clk_init(struct platform_device *pdev,
struct cygnus_audio *cygaud)
{
int i;
char clk_name[PROP_LEN_MAX];
for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(cygaud->audio_clk[i]))
return PTR_ERR(cygaud->audio_clk[i]);
}
return 0;
}
static int cygnus_ssp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child_node;
struct resource *res = pdev->resource;
struct cygnus_audio *cygaud;
int err = -EINVAL;
int node_count;
int active_port_count;
cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
if (!cygaud)
return -ENOMEM;
dev_set_drvdata(dev, cygaud);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
cygaud->audio = devm_ioremap_resource(dev, res);
if (IS_ERR(cygaud->audio))
return PTR_ERR(cygaud->audio);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in");
cygaud->i2s_in = devm_ioremap_resource(dev, res);
if (IS_ERR(cygaud->i2s_in))
return PTR_ERR(cygaud->i2s_in);
/* Tri-state all controlable pins until we know that we need them */
writel(CYGNUS_SSP_TRISTATE_MASK,
cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
node_count = of_get_child_count(pdev->dev.of_node);
if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
dev_err(dev, "child nodes is %d. Must be between 1 and %d\n",
node_count, CYGNUS_MAX_PORTS);
return -EINVAL;
}
active_port_count = 0;
for_each_available_child_of_node(pdev->dev.of_node, child_node) {
err = parse_ssp_child_node(pdev, child_node, cygaud,
&cygnus_ssp_dai[active_port_count]);
/* negative is err, 0 is active and good, 1 is disabled */
if (err < 0)
return err;
else if (!err) {
dev_dbg(dev, "Activating DAI: %s\n",
cygnus_ssp_dai[active_port_count].name);
active_port_count++;
}
}
cygaud->dev = dev;
cygaud->active_ports = 0;
dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
err = snd_soc_register_component(dev, &cygnus_ssp_component,
cygnus_ssp_dai, active_port_count);
if (err) {
dev_err(dev, "snd_soc_register_dai failed\n");
return err;
}
cygaud->irq_num = platform_get_irq(pdev, 0);
if (cygaud->irq_num <= 0) {
dev_err(dev, "platform_get_irq failed\n");
err = cygaud->irq_num;
goto err_irq;
}
err = audio_clk_init(pdev, cygaud);
if (err) {
dev_err(dev, "audio clock initialization failed\n");
goto err_irq;
}
err = cygnus_soc_platform_register(dev, cygaud);
if (err) {
dev_err(dev, "platform reg error %d\n", err);
goto err_irq;
}
return 0;
err_irq:
snd_soc_unregister_component(dev);
return err;
}
static int cygnus_ssp_remove(struct platform_device *pdev)
{
cygnus_soc_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
static const struct of_device_id cygnus_ssp_of_match[] = {
{ .compatible = "brcm,cygnus-audio" },
{},
};
MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
static struct platform_driver cygnus_ssp_driver = {
.probe = cygnus_ssp_probe,
.remove = cygnus_ssp_remove,
.driver = {
.name = "cygnus-ssp",
.of_match_table = cygnus_ssp_of_match,
},
};
module_platform_driver(cygnus_ssp_driver);
MODULE_ALIAS("platform:cygnus-ssp");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");
/*
* Copyright (C) 2014-2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __CYGNUS_SSP_H__
#define __CYGNUS_SSP_H__
#define CYGNUS_TDM_DAI_MAX_SLOTS 16
#define CYGNUS_MAX_PLAYBACK_PORTS 4
#define CYGNUS_MAX_CAPTURE_PORTS 3
#define CYGNUS_MAX_I2S_PORTS 3
#define CYGNUS_MAX_PORTS CYGNUS_MAX_PLAYBACK_PORTS
#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
#define CYGNUS_SSP_FRAMEBITS_DIV 1
#define CYGNUS_SSPMODE_I2S 0
#define CYGNUS_SSPMODE_TDM 1
#define CYGNUS_SSPMODE_UNKNOWN -1
#define CYGNUS_SSP_CLKSRC_PLL 0
/* Max string length of our dt property names */
#define PROP_LEN_MAX 40
struct ringbuf_regs {
unsigned rdaddr;
unsigned wraddr;
unsigned baseaddr;
unsigned endaddr;
unsigned fmark; /* freemark for play, fullmark for caputure */
unsigned period_bytes;
unsigned buf_size;
};
#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
.rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
.wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
.baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
.endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
.fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
.period_bytes = 0, \
.buf_size = 0, \
})
#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs) { \
.rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
.wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
.baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
.endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
.fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
.period_bytes = 0, \
.buf_size = 0, \
})
enum cygnus_audio_port_type {
PORT_TDM,
PORT_SPDIF,
};
struct cygnus_ssp_regs {
u32 i2s_stream_cfg;
u32 i2s_cfg;
u32 i2s_cap_stream_cfg;
u32 i2s_cap_cfg;
u32 i2s_mclk_cfg;
u32 bf_destch_ctrl;
u32 bf_destch_cfg;
u32 bf_sourcech_ctrl;
u32 bf_sourcech_cfg;
u32 bf_sourcech_grp;
};
struct cygnus_track_clk {
bool cap_en;
bool play_en;
bool cap_clk_en;
bool play_clk_en;
};
struct cygnus_aio_port {
int portnum;
int mode;
bool is_slave;
int streams_on; /* will be 0 if both capture and play are off */
int fsync_width;
int port_type;
u32 mclk;
u32 lrclk;
u32 bit_per_frame;
u32 pll_clk_num;
struct cygnus_audio *cygaud;
struct cygnus_ssp_regs regs;
struct ringbuf_regs play_rb_regs;
struct ringbuf_regs capture_rb_regs;
struct snd_pcm_substream *play_stream;
struct snd_pcm_substream *capture_stream;
struct cygnus_track_clk clk_trace;
};
struct cygnus_audio {
struct cygnus_aio_port portinfo[CYGNUS_MAX_PORTS];
int irq_num;
void __iomem *audio;
struct device *dev;
void __iomem *i2s_in;
struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
int active_ports;
unsigned long vco_rate;
};
extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
int len);
extern int cygnus_soc_platform_register(struct device *dev,
struct cygnus_audio *cygaud);
extern int cygnus_soc_platform_unregister(struct device *dev);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
int len);
#endif
...@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS ...@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CS4349 if I2C select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CS47L24 if MFD_CS47L24
select SND_SOC_CS53L30 if I2C
select SND_SOC_CX20442 if TTY select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C select SND_SOC_DA7213 if I2C
...@@ -464,6 +465,11 @@ config SND_SOC_CS4349 ...@@ -464,6 +465,11 @@ config SND_SOC_CS4349
config SND_SOC_CS47L24 config SND_SOC_CS47L24
tristate tristate
# Cirrus Logic Quad-Channel ADC
config SND_SOC_CS53L30
tristate "Cirrus Logic CS53L30 CODEC"
depends on I2C
config SND_SOC_CX20442 config SND_SOC_CX20442
tristate tristate
depends on TTY depends on TTY
......
...@@ -52,6 +52,7 @@ snd-soc-cs42xx8-objs := cs42xx8.o ...@@ -52,6 +52,7 @@ snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cs4349-objs := cs4349.o snd-soc-cs4349-objs := cs4349.o
snd-soc-cs47l24-objs := cs47l24.o snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cs53l30-objs := cs53l30.o
snd-soc-cx20442-objs := cx20442.o snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o snd-soc-da7213-objs := da7213.o
...@@ -270,6 +271,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o ...@@ -270,6 +271,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
......
/*
* cs53l30.c -- CS53l30 ALSA Soc Audio driver
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
* Tim Howe <Tim.Howe@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "cs53l30.h"
#define CS53L30_NUM_SUPPLIES 2
static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
"VA",
"VP",
};
struct cs53l30_private {
struct regulator_bulk_data supplies[CS53L30_NUM_SUPPLIES];
struct regmap *regmap;
struct gpio_desc *reset_gpio;
struct gpio_desc *mute_gpio;
struct clk *mclk;
bool use_sdout2;
u32 mclk_rate;
};
static const struct reg_default cs53l30_reg_defaults[] = {
{ CS53L30_PWRCTL, CS53L30_PWRCTL_DEFAULT },
{ CS53L30_MCLKCTL, CS53L30_MCLKCTL_DEFAULT },
{ CS53L30_INT_SR_CTL, CS53L30_INT_SR_CTL_DEFAULT },
{ CS53L30_MICBIAS_CTL, CS53L30_MICBIAS_CTL_DEFAULT },
{ CS53L30_ASPCFG_CTL, CS53L30_ASPCFG_CTL_DEFAULT },
{ CS53L30_ASP_CTL1, CS53L30_ASP_CTL1_DEFAULT },
{ CS53L30_ASP_TDMTX_CTL1, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
{ CS53L30_ASP_TDMTX_CTL2, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
{ CS53L30_ASP_TDMTX_CTL3, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
{ CS53L30_ASP_TDMTX_CTL4, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN1, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN2, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN3, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN4, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN5, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_TDMTX_EN6, CS53L30_ASP_TDMTX_ENx_DEFAULT },
{ CS53L30_ASP_CTL2, CS53L30_ASP_CTL2_DEFAULT },
{ CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DEFAULT },
{ CS53L30_LRCK_CTL1, CS53L30_LRCK_CTLx_DEFAULT },
{ CS53L30_LRCK_CTL2, CS53L30_LRCK_CTLx_DEFAULT },
{ CS53L30_MUTEP_CTL1, CS53L30_MUTEP_CTL1_DEFAULT },
{ CS53L30_MUTEP_CTL2, CS53L30_MUTEP_CTL2_DEFAULT },
{ CS53L30_INBIAS_CTL1, CS53L30_INBIAS_CTL1_DEFAULT },
{ CS53L30_INBIAS_CTL2, CS53L30_INBIAS_CTL2_DEFAULT },
{ CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_STR_CTL_DEFAULT },
{ CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_STR_CTL_DEFAULT },
{ CS53L30_ADCDMIC1_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
{ CS53L30_ADCDMIC1_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
{ CS53L30_ADC1_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
{ CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
{ CS53L30_ADC1A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
{ CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
{ CS53L30_ADC1A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
{ CS53L30_ADC1B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
{ CS53L30_ADCDMIC2_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
{ CS53L30_ADCDMIC2_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
{ CS53L30_ADC2_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
{ CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
{ CS53L30_ADC2A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
{ CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
{ CS53L30_ADC2A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
{ CS53L30_ADC2B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
{ CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK },
};
static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
{
if (reg == CS53L30_IS)
return true;
else
return false;
}
static bool cs53l30_writeable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS53L30_DEVID_AB:
case CS53L30_DEVID_CD:
case CS53L30_DEVID_E:
case CS53L30_REVID:
case CS53L30_IS:
return false;
default:
return true;
}
}
static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS53L30_DEVID_AB:
case CS53L30_DEVID_CD:
case CS53L30_DEVID_E:
case CS53L30_REVID:
case CS53L30_PWRCTL:
case CS53L30_MCLKCTL:
case CS53L30_INT_SR_CTL:
case CS53L30_MICBIAS_CTL:
case CS53L30_ASPCFG_CTL:
case CS53L30_ASP_CTL1:
case CS53L30_ASP_TDMTX_CTL1:
case CS53L30_ASP_TDMTX_CTL2:
case CS53L30_ASP_TDMTX_CTL3:
case CS53L30_ASP_TDMTX_CTL4:
case CS53L30_ASP_TDMTX_EN1:
case CS53L30_ASP_TDMTX_EN2:
case CS53L30_ASP_TDMTX_EN3:
case CS53L30_ASP_TDMTX_EN4:
case CS53L30_ASP_TDMTX_EN5:
case CS53L30_ASP_TDMTX_EN6:
case CS53L30_ASP_CTL2:
case CS53L30_SFT_RAMP:
case CS53L30_LRCK_CTL1:
case CS53L30_LRCK_CTL2:
case CS53L30_MUTEP_CTL1:
case CS53L30_MUTEP_CTL2:
case CS53L30_INBIAS_CTL1:
case CS53L30_INBIAS_CTL2:
case CS53L30_DMIC1_STR_CTL:
case CS53L30_DMIC2_STR_CTL:
case CS53L30_ADCDMIC1_CTL1:
case CS53L30_ADCDMIC1_CTL2:
case CS53L30_ADC1_CTL3:
case CS53L30_ADC1_NG_CTL:
case CS53L30_ADC1A_AFE_CTL:
case CS53L30_ADC1B_AFE_CTL:
case CS53L30_ADC1A_DIG_VOL:
case CS53L30_ADC1B_DIG_VOL:
case CS53L30_ADCDMIC2_CTL1:
case CS53L30_ADCDMIC2_CTL2:
case CS53L30_ADC2_CTL3:
case CS53L30_ADC2_NG_CTL:
case CS53L30_ADC2A_AFE_CTL:
case CS53L30_ADC2B_AFE_CTL:
case CS53L30_ADC2A_DIG_VOL:
case CS53L30_ADC2B_DIG_VOL:
case CS53L30_INT_MASK:
return true;
default:
return false;
}
}
static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0);
static const char * const input1_sel_text[] = {
"DMIC1 On AB In",
"DMIC1 On A In",
"DMIC1 On B In",
"ADC1 On AB In",
"ADC1 On A In",
"ADC1 On B In",
"DMIC1 Off ADC1 Off",
};
static unsigned int const input1_sel_values[] = {
CS53L30_CH_TYPE,
CS53L30_ADCxB_PDN | CS53L30_CH_TYPE,
CS53L30_ADCxA_PDN | CS53L30_CH_TYPE,
CS53L30_DMICx_PDN,
CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
};
static const char * const input2_sel_text[] = {
"DMIC2 On AB In",
"DMIC2 On A In",
"DMIC2 On B In",
"ADC2 On AB In",
"ADC2 On A In",
"ADC2 On B In",
"DMIC2 Off ADC2 Off",
};
static unsigned int const input2_sel_values[] = {
0x0,
CS53L30_ADCxB_PDN,
CS53L30_ADCxA_PDN,
CS53L30_DMICx_PDN,
CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
};
static const char * const input1_route_sel_text[] = {
"ADC1_SEL", "DMIC1_SEL",
};
static const struct soc_enum input1_route_sel_enum =
SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT,
ARRAY_SIZE(input1_route_sel_text),
input1_route_sel_text);
static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
CS53L30_ADCDMICx_PDN_MASK, input1_sel_text,
input1_sel_values);
static const struct snd_kcontrol_new input1_route_sel_mux =
SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
static const char * const input2_route_sel_text[] = {
"ADC2_SEL", "DMIC2_SEL",
};
/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
static const struct soc_enum input2_route_sel_enum =
SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
ARRAY_SIZE(input2_route_sel_text),
input2_route_sel_text);
static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
CS53L30_ADCDMICx_PDN_MASK, input2_sel_text,
input2_sel_values);
static const struct snd_kcontrol_new input2_route_sel_mux =
SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
/*
* TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
* TB - Time base
* NOTE: If MCLK_INT_SCALE = 0, then TB=1
*/
static const char * const cs53l30_ng_delay_text[] = {
"TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms",
};
static const struct soc_enum adc1_ng_delay_enum =
SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
ARRAY_SIZE(cs53l30_ng_delay_text),
cs53l30_ng_delay_text);
static const struct soc_enum adc2_ng_delay_enum =
SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
ARRAY_SIZE(cs53l30_ng_delay_text),
cs53l30_ng_delay_text);
/* The noise gate threshold selected will depend on NG Boost */
static const char * const cs53l30_ng_thres_text[] = {
"-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
"-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB",
};
static const struct soc_enum adc1_ng_thres_enum =
SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
ARRAY_SIZE(cs53l30_ng_thres_text),
cs53l30_ng_thres_text);
static const struct soc_enum adc2_ng_thres_enum =
SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
ARRAY_SIZE(cs53l30_ng_thres_text),
cs53l30_ng_thres_text);
/* Corner frequencies are with an Fs of 48kHz. */
static const char * const hpf_corner_freq_text[] = {
"1.86Hz", "120Hz", "235Hz", "466Hz",
};
static const struct soc_enum adc1_hpf_enum =
SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
static const struct soc_enum adc2_hpf_enum =
SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP,
CS53L30_DIGSFT_SHIFT, 1, 0),
SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3,
CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3,
CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
CS53L30_ADCxA_NG_SHIFT, 1, 0),
SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
CS53L30_ADCxB_NG_SHIFT, 1, 0),
SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
CS53L30_ADCxA_NG_SHIFT, 1, 0),
SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
CS53L30_ADCxB_NG_SHIFT, 1, 0),
SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2,
CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2,
CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2,
CS53L30_ADCxA_INV_SHIFT, 1, 0),
SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2,
CS53L30_ADCxB_INV_SHIFT, 1, 0),
SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2,
CS53L30_ADCxA_INV_SHIFT, 1, 0),
SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2,
CS53L30_ADCxB_INV_SHIFT, 1, 0),
SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL,
CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL,
CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL,
CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
2, 0, pga_preamp_tlv),
SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL,
CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
2, 0, pga_preamp_tlv),
SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
};
static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1_DMIC1"),
SND_SOC_DAPM_INPUT("IN2"),
SND_SOC_DAPM_INPUT("IN3_DMIC2"),
SND_SOC_DAPM_INPUT("IN4"),
SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL,
CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL,
CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL,
CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL,
CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1,
CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2,
CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
&input1_route_sel_mux),
SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
&input2_route_sel_mux),
SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1,
CS53L30_ADCxA_PDN_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1,
CS53L30_ADCxB_PDN_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1,
CS53L30_ADCxA_PDN_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1,
CS53L30_ADCxB_PDN_SHIFT, 1),
SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1,
CS53L30_DMICx_PDN_SHIFT, 1),
SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1,
CS53L30_DMICx_PDN_SHIFT, 1),
};
static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = {
/* ADC Input Paths */
{"ADC1A", NULL, "IN1_DMIC1"},
{"Input Mux 1", "ADC1_SEL", "ADC1A"},
{"ADC1B", NULL, "IN2"},
{"ADC2A", NULL, "IN3_DMIC2"},
{"Input Mux 2", "ADC2_SEL", "ADC2A"},
{"ADC2B", NULL, "IN4"},
/* MIC Bias Paths */
{"ADC1A", NULL, "MIC1 Bias"},
{"ADC1B", NULL, "MIC2 Bias"},
{"ADC2A", NULL, "MIC3 Bias"},
{"ADC2B", NULL, "MIC4 Bias"},
/* DMIC Paths */
{"DMIC1", NULL, "IN1_DMIC1"},
{"Input Mux 1", "DMIC1_SEL", "DMIC1"},
{"DMIC2", NULL, "IN3_DMIC2"},
{"Input Mux 2", "DMIC2_SEL", "DMIC2"},
};
static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = {
/* Output Paths when using SDOUT1 only */
{"ASP_SDOUT1", NULL, "ADC1A" },
{"ASP_SDOUT1", NULL, "Input Mux 1"},
{"ASP_SDOUT1", NULL, "ADC1B"},
{"ASP_SDOUT1", NULL, "ADC2A"},
{"ASP_SDOUT1", NULL, "Input Mux 2"},
{"ASP_SDOUT1", NULL, "ADC2B"},
{"Capture", NULL, "ASP_SDOUT1"},
};
static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = {
/* Output Paths when using both SDOUT1 and SDOUT2 */
{"ASP_SDOUT1", NULL, "ADC1A" },
{"ASP_SDOUT1", NULL, "Input Mux 1"},
{"ASP_SDOUT1", NULL, "ADC1B"},
{"ASP_SDOUT2", NULL, "ADC2A"},
{"ASP_SDOUT2", NULL, "Input Mux 2"},
{"ASP_SDOUT2", NULL, "ADC2B"},
{"Capture", NULL, "ASP_SDOUT1"},
{"Capture", NULL, "ASP_SDOUT2"},
};
struct cs53l30_mclk_div {
u32 mclk_rate;
u32 srate;
u8 asp_rate;
u8 internal_fs_ratio;
u8 mclk_int_scale;
};
static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
/* NOTE: Enable MCLK_INT_SCALE to save power. */
/* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
{5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6000000, 8000, 0x1, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE},
{6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE},
{6144000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
{6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
};
struct cs53l30_mclkx_div {
u32 mclkx;
u8 ratio;
u8 mclkdiv;
};
static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
{5644800, 1, CS53L30_MCLK_DIV_BY_1},
{6000000, 1, CS53L30_MCLK_DIV_BY_1},
{6144000, 1, CS53L30_MCLK_DIV_BY_1},
{11289600, 2, CS53L30_MCLK_DIV_BY_2},
{12288000, 2, CS53L30_MCLK_DIV_BY_2},
{12000000, 2, CS53L30_MCLK_DIV_BY_2},
{19200000, 3, CS53L30_MCLK_DIV_BY_3},
};
static int cs53l30_get_mclkx_coeff(int mclkx)
{
int i;
for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
return i;
}
return -EINVAL;
}
static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
{
int i;
for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
cs53l30_mclk_coeffs[i].srate == srate)
return i;
}
return -EINVAL;
}
static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
int mclkx_coeff;
u32 mclk_rate;
/* MCLKX -> MCLK */
mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
if (mclkx_coeff < 0)
return mclkx_coeff;
mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_DIV_MASK,
cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv);
priv->mclk_rate = mclk_rate;
return 0;
}
static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
u8 aspcfg = 0, aspctl1 = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
aspcfg |= CS53L30_ASP_MS;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* Set TDM_PDN to turn off TDM mode -- Reset default */
aspctl1 |= CS53L30_ASP_TDM_PDN;
break;
case SND_SOC_DAIFMT_DSP_A:
/*
* Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
* with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
* the CS53L30 datasheet
*/
aspctl1 |= CS53L30_SHIFT_LEFT;
break;
default:
return -EINVAL;
}
/* Check to see if the SCLK is inverted */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_NF:
case SND_SOC_DAIFMT_IB_IF:
aspcfg ^= CS53L30_ASP_SCLK_INV;
break;
default:
break;
}
regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
return 0;
}
static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
int srate = params_rate(params);
int mclk_coeff;
/* MCLK -> srate */
mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate);
if (mclk_coeff < 0)
return -EINVAL;
regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
CS53L30_INTRNL_FS_RATIO_MASK,
cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio);
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_INT_SCALE_MASK,
cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale);
regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
CS53L30_ASP_RATE_MASK,
cs53l30_mclk_coeffs[mclk_coeff].asp_rate);
return 0;
}
static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
int i, inter_max_check, ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_LP_MASK, 0);
break;
case SND_SOC_BIAS_STANDBY:
if (dapm->bias_level == SND_SOC_BIAS_OFF) {
ret = clk_prepare_enable(priv->mclk);
if (ret) {
dev_err(codec->dev,
"failed to enable MCLK: %d\n", ret);
return ret;
}
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_DIS_MASK, 0);
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK, 0);
msleep(50);
} else {
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK,
CS53L30_PDN_ULP);
}
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
CS53L30_PDN_DONE, 0);
/*
* If digital softramp is set, the amount of time required
* for power down increases and depends on the digital
* volume setting.
*/
/* Set the max possible time if digsft is set */
regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
if (reg & CS53L30_DIGSFT_MASK)
inter_max_check = CS53L30_PDN_POLL_MAX;
else
inter_max_check = 10;
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK,
CS53L30_PDN_ULP);
/* PDN_DONE will take a min of 20ms to be set.*/
msleep(20);
/* Clr status */
regmap_read(priv->regmap, CS53L30_IS, &reg);
for (i = 0; i < inter_max_check; i++) {
if (inter_max_check < 10) {
usleep_range(1000, 1100);
regmap_read(priv->regmap, CS53L30_IS, &reg);
if (reg & CS53L30_PDN_DONE)
break;
} else {
usleep_range(10000, 10100);
regmap_read(priv->regmap, CS53L30_IS, &reg);
if (reg & CS53L30_PDN_DONE)
break;
}
}
/* PDN_DONE is set. We now can disable the MCLK */
regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
CS53L30_PDN_DONE, CS53L30_PDN_DONE);
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_DIS_MASK,
CS53L30_MCLK_DIS);
clk_disable_unprepare(priv->mclk);
break;
}
return 0;
}
static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
u8 val = tristate ? CS53L30_ASP_3ST : 0;
return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
CS53L30_ASP_3ST_MASK, val);
}
static unsigned int const cs53l30_src_rates[] = {
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
};
static struct snd_pcm_hw_constraint_list src_constraints = {
.count = ARRAY_SIZE(cs53l30_src_rates),
.list = cs53l30_src_rates,
};
static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &src_constraints);
return 0;
}
/*
* Note: CS53L30 counts the slot number per byte while ASoC counts the slot
* number per slot_width. So there is a difference between the slots of ASoC
* and the slots of CS53L30.
*/
static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48};
unsigned int slot_next, slot_step;
u64 tx_enable = 0;
int i;
if (!rx_mask) {
dev_err(dai->dev, "rx masks must not be 0\n");
return -EINVAL;
}
/* Assuming slot_width is not supposed to be greater than 64 */
if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
dev_err(dai->dev, "invalid slot number or slot width\n");
return -EINVAL;
}
if (slot_width & 0x7) {
dev_err(dai->dev, "slot width must count in byte\n");
return -EINVAL;
}
/* How many bytes in each ASoC slot */
slot_step = slot_width >> 3;
for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) {
/* Find the first slot from LSB */
slot_next = __ffs(rx_mask);
/* Save the slot location by converting to CS53L30 slot */
loc[i] = slot_next * slot_step;
/* Create the mask of CS53L30 slot */
tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i];
/* Clear this slot from rx_mask */
rx_mask &= ~(1 << slot_next);
}
/* Error out to avoid slot shift */
if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
CS53L30_TDM_SLOT_MAX);
return -EINVAL;
}
/* Validate the last active CS53L30 slot */
slot_next = loc[i - 1] + slot_step - 1;
if (slot_next > 47) {
dev_err(dai->dev, "slot selection out of bounds: %u\n",
slot_next);
return -EINVAL;
}
for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
}
for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
tx_enable & 0xff);
tx_enable >>= 8;
dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
}
return 0;
}
static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
if (priv->mute_gpio)
gpiod_set_value_cansleep(priv->mute_gpio, mute);
return 0;
}
/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops cs53l30_ops = {
.startup = cs53l30_pcm_startup,
.hw_params = cs53l30_pcm_hw_params,
.set_fmt = cs53l30_set_dai_fmt,
.set_sysclk = cs53l30_set_sysclk,
.set_tristate = cs53l30_set_tristate,
.set_tdm_slot = cs53l30_set_dai_tdm_slot,
.mute_stream = cs53l30_mute_stream,
};
static struct snd_soc_dai_driver cs53l30_dai = {
.name = "cs53l30",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 4,
.rates = CS53L30_RATES,
.formats = CS53L30_FORMATS,
},
.ops = &cs53l30_ops,
.symmetric_rates = 1,
};
static int cs53l30_codec_probe(struct snd_soc_codec *codec)
{
struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
if (priv->use_sdout2)
snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2,
ARRAY_SIZE(cs53l30_dapm_routes_sdout2));
else
snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1,
ARRAY_SIZE(cs53l30_dapm_routes_sdout1));
return 0;
}
static struct snd_soc_codec_driver cs53l30_driver = {
.probe = cs53l30_codec_probe,
.set_bias_level = cs53l30_set_bias_level,
.idle_bias_off = true,
.dapm_widgets = cs53l30_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
.dapm_routes = cs53l30_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes),
.controls = cs53l30_snd_controls,
.num_controls = ARRAY_SIZE(cs53l30_snd_controls),
};
static struct regmap_config cs53l30_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS53L30_MAX_REGISTER,
.reg_defaults = cs53l30_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
.volatile_reg = cs53l30_volatile_register,
.writeable_reg = cs53l30_writeable_register,
.readable_reg = cs53l30_readable_register,
.cache_type = REGCACHE_RBTREE,
};
static int cs53l30_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct device_node *np = client->dev.of_node;
struct device *dev = &client->dev;
struct cs53l30_private *cs53l30;
unsigned int devid = 0;
unsigned int reg;
int ret = 0, i;
u8 val;
cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
if (!cs53l30)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
cs53l30->supplies[i].supply = cs53l30_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
if (ret) {
dev_err(dev, "failed to get supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
return ret;
}
/* Reset the Device */
cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(cs53l30->reset_gpio)) {
ret = PTR_ERR(cs53l30->reset_gpio);
goto error;
}
if (cs53l30->reset_gpio)
gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
i2c_set_clientdata(client, cs53l30);
cs53l30->mclk_rate = 0;
cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
if (IS_ERR(cs53l30->regmap)) {
ret = PTR_ERR(cs53l30->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret);
goto error;
}
/* Initialize codec */
ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
devid = reg << 12;
ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
devid |= reg << 4;
ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
devid |= (reg & 0xF0) >> 4;
if (devid != CS53L30_DEVID) {
ret = -ENODEV;
dev_err(dev, "Device ID (%X). Expected %X\n",
devid, CS53L30_DEVID);
goto error;
}
ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
if (ret < 0) {
dev_err(dev, "failed to get Revision ID: %d\n", ret);
goto error;
}
/* Check if MCLK provided */
cs53l30->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(cs53l30->mclk)) {
if (PTR_ERR(cs53l30->mclk) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto error;
}
/* Otherwise mark the mclk pointer to NULL */
cs53l30->mclk = NULL;
}
/* Fetch the MUTE control */
cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
GPIOD_OUT_HIGH);
if (IS_ERR(cs53l30->mute_gpio)) {
ret = PTR_ERR(cs53l30->mute_gpio);
goto error;
}
if (cs53l30->mute_gpio) {
/* Enable MUTE controls via MUTE pin */
regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
CS53L30_MUTEP_CTL1_MUTEALL);
/* Flip the polarity of MUTE pin */
if (gpiod_is_active_low(cs53l30->mute_gpio))
regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
CS53L30_MUTE_PIN_POLARITY, 0);
}
if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
CS53L30_MIC_BIAS_CTRL_MASK, val);
if (of_property_read_bool(np, "cirrus,use-sdout2"))
cs53l30->use_sdout2 = true;
dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
ret = snd_soc_register_codec(dev, &cs53l30_driver, &cs53l30_dai, 1);
if (ret) {
dev_err(dev, "failed to register codec: %d\n", ret);
goto error;
}
return 0;
error:
regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
return ret;
}
static int cs53l30_i2c_remove(struct i2c_client *client)
{
struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev);
/* Hold down reset */
if (cs53l30->reset_gpio)
gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
return 0;
}
#ifdef CONFIG_PM
static int cs53l30_runtime_suspend(struct device *dev)
{
struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
regcache_cache_only(cs53l30->regmap, true);
/* Hold down reset */
if (cs53l30->reset_gpio)
gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
return 0;
}
static int cs53l30_runtime_resume(struct device *dev)
{
struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
return ret;
}
if (cs53l30->reset_gpio)
gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
regcache_cache_only(cs53l30->regmap, false);
ret = regcache_sync(cs53l30->regmap);
if (ret) {
dev_err(dev, "failed to synchronize regcache: %d\n", ret);
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops cs53l30_runtime_pm = {
SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
NULL)
};
static const struct of_device_id cs53l30_of_match[] = {
{ .compatible = "cirrus,cs53l30", },
{},
};
MODULE_DEVICE_TABLE(of, cs53l30_of_match);
static const struct i2c_device_id cs53l30_id[] = {
{ "cs53l30", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, cs53l30_id);
static struct i2c_driver cs53l30_i2c_driver = {
.driver = {
.name = "cs53l30",
.pm = &cs53l30_runtime_pm,
},
.id_table = cs53l30_id,
.probe = cs53l30_i2c_probe,
.remove = cs53l30_i2c_remove,
};
module_i2c_driver(cs53l30_i2c_driver);
MODULE_DESCRIPTION("ASoC CS53L30 driver");
MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
MODULE_LICENSE("GPL");
/*
* ALSA SoC CS53L30 codec driver
*
* Copyright 2015 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
* Tim Howe <Tim.Howe@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __CS53L30_H__
#define __CS53L30_H__
/* I2C Registers */
#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */
#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */
#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */
#define CS53L30_REVID 0x05 /* Revision ID [RO]. */
#define CS53L30_PWRCTL 0x06 /* Power Control. */
#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */
#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */
#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */
#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */
#define CS53L30_ASP_CTL1 0x0D /* ASP1 Control. */
#define CS53L30_ASP_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */
#define CS53L30_ASP_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */
#define CS53L30_ASP_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */
#define CS53L30_ASP_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */
#define CS53L30_ASP_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */
#define CS53L30_ASP_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */
#define CS53L30_ASP_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */
#define CS53L30_ASP_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */
#define CS53L30_ASP_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */
#define CS53L30_ASP_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */
#define CS53L30_ASP_CTL2 0x18 /* ASP2 Control. */
#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */
#define CS53L30_LRCK_CTL1 0x1B /* LRCK Control 1. */
#define CS53L30_LRCK_CTL2 0x1C /* LRCK Control 2. */
#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */
#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */
#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */
#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */
#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */
#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */
#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */
#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */
#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */
#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */
#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */
#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */
#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */
#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */
#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */
#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */
#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */
#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */
#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */
#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */
#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */
#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */
#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */
#define CS53L30_IS 0x36 /* Interrupt Status. */
#define CS53L30_MAX_REGISTER 0x36
#define CS53L30_TDM_SLOT_MAX 4
#define CS53L30_ASP_TDMTX_CTL(x) (CS53L30_ASP_TDMTX_CTL1 + (x))
/* x : index for registers; n : index for slot; 8 slots per register */
#define CS53L30_ASP_TDMTX_ENx(x) (CS53L30_ASP_TDMTX_EN6 - (x))
#define CS53L30_ASP_TDMTX_ENn(n) CS53L30_ASP_TDMTX_ENx((n) >> 3)
#define CS53L30_ASP_TDMTX_ENx_MAX 6
/* Device ID */
#define CS53L30_DEVID 0x53A30
/* PDN_DONE Poll Maximum
* If soft ramp is set it will take much longer to power down
* the system.
*/
#define CS53L30_PDN_POLL_MAX 90
/* Bitfield Definitions */
/* R6 (0x06) CS53L30_PWRCTL - Power Control */
#define CS53L30_PDN_ULP_SHIFT 7
#define CS53L30_PDN_ULP_MASK (1 << CS53L30_PDN_ULP_SHIFT)
#define CS53L30_PDN_ULP (1 << CS53L30_PDN_ULP_SHIFT)
#define CS53L30_PDN_LP_SHIFT 6
#define CS53L30_PDN_LP_MASK (1 << CS53L30_PDN_LP_SHIFT)
#define CS53L30_PDN_LP (1 << CS53L30_PDN_LP_SHIFT)
#define CS53L30_DISCHARGE_FILT_SHIFT 5
#define CS53L30_DISCHARGE_FILT_MASK (1 << CS53L30_DISCHARGE_FILT_SHIFT)
#define CS53L30_DISCHARGE_FILT (1 << CS53L30_DISCHARGE_FILT_SHIFT)
#define CS53L30_THMS_PDN_SHIFT 4
#define CS53L30_THMS_PDN_MASK (1 << CS53L30_THMS_PDN_SHIFT)
#define CS53L30_THMS_PDN (1 << CS53L30_THMS_PDN_SHIFT)
#define CS53L30_PWRCTL_DEFAULT (CS53L30_THMS_PDN)
/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */
#define CS53L30_MCLK_DIS_SHIFT 7
#define CS53L30_MCLK_DIS_MASK (1 << CS53L30_MCLK_DIS_SHIFT)
#define CS53L30_MCLK_DIS (1 << CS53L30_MCLK_DIS_SHIFT)
#define CS53L30_MCLK_INT_SCALE_SHIFT 6
#define CS53L30_MCLK_INT_SCALE_MASK (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
#define CS53L30_MCLK_INT_SCALE (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
#define CS53L30_DMIC_DRIVE_SHIFT 5
#define CS53L30_DMIC_DRIVE_MASK (1 << CS53L30_DMIC_DRIVE_SHIFT)
#define CS53L30_DMIC_DRIVE (1 << CS53L30_DMIC_DRIVE_SHIFT)
#define CS53L30_MCLK_DIV_SHIFT 2
#define CS53L30_MCLK_DIV_WIDTH 2
#define CS53L30_MCLK_DIV_MASK (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT)
#define CS53L30_MCLK_DIV_BY_1 (0x0 << CS53L30_MCLK_DIV_SHIFT)
#define CS53L30_MCLK_DIV_BY_2 (0x1 << CS53L30_MCLK_DIV_SHIFT)
#define CS53L30_MCLK_DIV_BY_3 (0x2 << CS53L30_MCLK_DIV_SHIFT)
#define CS53L30_SYNC_EN_SHIFT 1
#define CS53L30_SYNC_EN_MASK (1 << CS53L30_SYNC_EN_SHIFT)
#define CS53L30_SYNC_EN (1 << CS53L30_SYNC_EN_SHIFT)
#define CS53L30_MCLKCTL_DEFAULT (CS53L30_MCLK_DIV_BY_2)
/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */
#define CS53L30_INTRNL_FS_RATIO_SHIFT 4
#define CS53L30_INTRNL_FS_RATIO_MASK (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
#define CS53L30_INTRNL_FS_RATIO (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
#define CS53L30_MCLK_19MHZ_EN_SHIFT 0
#define CS53L30_MCLK_19MHZ_EN_MASK (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
#define CS53L30_MCLK_19MHZ_EN (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
/* 0x6 << 1 is reserved bits */
#define CS53L30_INT_SR_CTL_DEFAULT (CS53L30_INTRNL_FS_RATIO | 0x6 << 1)
/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */
#define CS53L30_MIC4_BIAS_PDN_SHIFT 7
#define CS53L30_MIC4_BIAS_PDN_MASK (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
#define CS53L30_MIC4_BIAS_PDN (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
#define CS53L30_MIC3_BIAS_PDN_SHIFT 6
#define CS53L30_MIC3_BIAS_PDN_MASK (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
#define CS53L30_MIC3_BIAS_PDN (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
#define CS53L30_MIC2_BIAS_PDN_SHIFT 5
#define CS53L30_MIC2_BIAS_PDN_MASK (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
#define CS53L30_MIC2_BIAS_PDN (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
#define CS53L30_MIC1_BIAS_PDN_SHIFT 4
#define CS53L30_MIC1_BIAS_PDN_MASK (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
#define CS53L30_MIC1_BIAS_PDN (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
#define CS53L30_MICx_BIAS_PDN (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT)
#define CS53L30_VP_MIN_SHIFT 2
#define CS53L30_VP_MIN_MASK (1 << CS53L30_VP_MIN_SHIFT)
#define CS53L30_VP_MIN (1 << CS53L30_VP_MIN_SHIFT)
#define CS53L30_MIC_BIAS_CTRL_SHIFT 0
#define CS53L30_MIC_BIAS_CTRL_WIDTH 2
#define CS53L30_MIC_BIAS_CTRL_MASK (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT)
#define CS53L30_MIC_BIAS_CTRL_HIZ (0 << CS53L30_MIC_BIAS_CTRL_SHIFT)
#define CS53L30_MIC_BIAS_CTRL_1V8 (1 << CS53L30_MIC_BIAS_CTRL_SHIFT)
#define CS53L30_MIC_BIAS_CTRL_2V75 (2 << CS53L30_MIC_BIAS_CTRL_SHIFT)
#define CS53L30_MICBIAS_CTL_DEFAULT (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN)
/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */
#define CS53L30_ASP_MS_SHIFT 7
#define CS53L30_ASP_MS_MASK (1 << CS53L30_ASP_MS_SHIFT)
#define CS53L30_ASP_MS (1 << CS53L30_ASP_MS_SHIFT)
#define CS53L30_ASP_SCLK_INV_SHIFT 4
#define CS53L30_ASP_SCLK_INV_MASK (1 << CS53L30_ASP_SCLK_INV_SHIFT)
#define CS53L30_ASP_SCLK_INV (1 << CS53L30_ASP_SCLK_INV_SHIFT)
#define CS53L30_ASP_RATE_SHIFT 0
#define CS53L30_ASP_RATE_WIDTH 4
#define CS53L30_ASP_RATE_MASK (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT)
#define CS53L30_ASP_RATE_48K (0xc << CS53L30_ASP_RATE_SHIFT)
#define CS53L30_ASPCFG_CTL_DEFAULT (CS53L30_ASP_RATE_48K)
/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */
#define CS53L30_ASP_TDM_PDN_SHIFT 7
#define CS53L30_ASP_TDM_PDN_MASK (1 << CS53L30_ASP_TDM_PDN_SHIFT)
#define CS53L30_ASP_TDM_PDN (1 << CS53L30_ASP_TDM_PDN_SHIFT)
#define CS53L30_ASP_SDOUTx_PDN_SHIFT 6
#define CS53L30_ASP_SDOUTx_PDN_MASK (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
#define CS53L30_ASP_SDOUTx_PDN (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
#define CS53L30_ASP_3ST_SHIFT 5
#define CS53L30_ASP_3ST_MASK (1 << CS53L30_ASP_3ST_SHIFT)
#define CS53L30_ASP_3ST (1 << CS53L30_ASP_3ST_SHIFT)
#define CS53L30_SHIFT_LEFT_SHIFT 4
#define CS53L30_SHIFT_LEFT_MASK (1 << CS53L30_SHIFT_LEFT_SHIFT)
#define CS53L30_SHIFT_LEFT (1 << CS53L30_SHIFT_LEFT_SHIFT)
#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0
#define CS53L30_ASP_SDOUTx_DRIVE_MASK (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
#define CS53L30_ASP_SDOUTx_DRIVE (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
#define CS53L30_ASP_CTL1_DEFAULT (CS53L30_ASP_TDM_PDN)
#define CS53L30_ASP_CTL2_DEFAULT (0)
/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */
#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7
#define CS53L30_ASP_CHx_TX_STATE_MASK (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
#define CS53L30_ASP_CHx_TX_STATE (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
#define CS53L30_ASP_CHx_TX_LOC_SHIFT 0
#define CS53L30_ASP_CHx_TX_LOC_WIDTH 6
#define CS53L30_ASP_CHx_TX_LOC_MASK (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
#define CS53L30_ASP_CHx_TX_LOC_MAX (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT)
#define CS53L30_ASP_CHx_TX_LOC(x) ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX)
/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */
#define CS53L30_ASP_TDMTX_ENx_DEFAULT (0)
/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */
#define CS53L30_DIGSFT_SHIFT 5
#define CS53L30_DIGSFT_MASK (1 << CS53L30_DIGSFT_SHIFT)
#define CS53L30_DIGSFT (1 << CS53L30_DIGSFT_SHIFT)
#define CS53L30_SFT_RMP_DEFAULT (0)
/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */
#define CS53L30_LRCK_50_NPW_SHIFT 3
#define CS53L30_LRCK_50_NPW_MASK (1 << CS53L30_LRCK_50_NPW_SHIFT)
#define CS53L30_LRCK_50_NPW (1 << CS53L30_LRCK_50_NPW_SHIFT)
#define CS53L30_LRCK_TPWH_SHIFT 0
#define CS53L30_LRCK_TPWH_WIDTH 3
#define CS53L30_LRCK_TPWH_MASK (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT)
#define CS53L30_LRCK_TPWH(x) (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK)
#define CS53L30_LRCK_CTLx_DEFAULT (0)
/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */
#define CS53L30_MUTE_PDN_ULP_SHIFT 7
#define CS53L30_MUTE_PDN_ULP_MASK (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
#define CS53L30_MUTE_PDN_ULP (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
#define CS53L30_MUTE_PDN_LP_SHIFT 6
#define CS53L30_MUTE_PDN_LP_MASK (1 << CS53L30_MUTE_PDN_LP_SHIFT)
#define CS53L30_MUTE_PDN_LP (1 << CS53L30_MUTE_PDN_LP_SHIFT)
#define CS53L30_MUTE_M4B_PDN_SHIFT 4
#define CS53L30_MUTE_M4B_PDN_MASK (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
#define CS53L30_MUTE_M4B_PDN (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
#define CS53L30_MUTE_M3B_PDN_SHIFT 3
#define CS53L30_MUTE_M3B_PDN_MASK (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
#define CS53L30_MUTE_M3B_PDN (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
#define CS53L30_MUTE_M2B_PDN_SHIFT 2
#define CS53L30_MUTE_M2B_PDN_MASK (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
#define CS53L30_MUTE_M2B_PDN (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
#define CS53L30_MUTE_M1B_PDN_SHIFT 1
#define CS53L30_MUTE_M1B_PDN_MASK (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
#define CS53L30_MUTE_M1B_PDN (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
/* Note: be careful - x starts from 0 */
#define CS53L30_MUTE_MxB_PDN_SHIFT(x) (CS53L30_MUTE_M1B_PDN_SHIFT + (x))
#define CS53L30_MUTE_MxB_PDN_MASK(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
#define CS53L30_MUTE_MxB_PDN(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
#define CS53L30_MUTE_MB_ALL_PDN_SHIFT 0
#define CS53L30_MUTE_MB_ALL_PDN_MASK (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
#define CS53L30_MUTE_MB_ALL_PDN (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
#define CS53L30_MUTEP_CTL1_MUTEALL (0xdf)
#define CS53L30_MUTEP_CTL1_DEFAULT (0)
/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */
#define CS53L30_MUTE_PIN_POLARITY_SHIFT 7
#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
#define CS53L30_MUTE_PIN_POLARITY (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6
#define CS53L30_MUTE_ASP_TDM_PDN_MASK (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
#define CS53L30_MUTE_ASP_TDM_PDN (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5
#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
#define CS53L30_MUTE_ASP_SDOUT2_PDN (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4
#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
#define CS53L30_MUTE_ASP_SDOUT1_PDN (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
/* Note: be careful - x starts from 0 */
#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
#define CS53L30_MUTE_ASP_SDOUTx_PDN (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
#define CS53L30_MUTE_ADC2B_PDN_SHIFT 3
#define CS53L30_MUTE_ADC2B_PDN_MASK (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
#define CS53L30_MUTE_ADC2B_PDN (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
#define CS53L30_MUTE_ADC2A_PDN_SHIFT 2
#define CS53L30_MUTE_ADC2A_PDN_MASK (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
#define CS53L30_MUTE_ADC2A_PDN (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
#define CS53L30_MUTE_ADC1B_PDN_SHIFT 1
#define CS53L30_MUTE_ADC1B_PDN_MASK (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
#define CS53L30_MUTE_ADC1B_PDN (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
#define CS53L30_MUTE_ADC1A_PDN_SHIFT 0
#define CS53L30_MUTE_ADC1A_PDN_MASK (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
#define CS53L30_MUTE_ADC1A_PDN (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
#define CS53L30_MUTEP_CTL2_DEFAULT (CS53L30_MUTE_PIN_POLARITY)
/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */
#define CS53L30_IN4M_BIAS_SHIFT 6
#define CS53L30_IN4M_BIAS_WIDTH 2
#define CS53L30_IN4M_BIAS_MASK (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
#define CS53L30_IN4M_BIAS_OPEN (0 << CS53L30_IN4M_BIAS_SHIFT)
#define CS53L30_IN4M_BIAS_PULL_DOWN (1 << CS53L30_IN4M_BIAS_SHIFT)
#define CS53L30_IN4M_BIAS_VCM (2 << CS53L30_IN4M_BIAS_SHIFT)
#define CS53L30_IN4P_BIAS_SHIFT 4
#define CS53L30_IN4P_BIAS_WIDTH 2
#define CS53L30_IN4P_BIAS_MASK (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT)
#define CS53L30_IN4P_BIAS_OPEN (0 << CS53L30_IN4P_BIAS_SHIFT)
#define CS53L30_IN4P_BIAS_PULL_DOWN (1 << CS53L30_IN4P_BIAS_SHIFT)
#define CS53L30_IN4P_BIAS_VCM (2 << CS53L30_IN4P_BIAS_SHIFT)
#define CS53L30_IN3M_BIAS_SHIFT 2
#define CS53L30_IN3M_BIAS_WIDTH 2
#define CS53L30_IN3M_BIAS_MASK (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
#define CS53L30_IN3M_BIAS_OPEN (0 << CS53L30_IN3M_BIAS_SHIFT)
#define CS53L30_IN3M_BIAS_PULL_DOWN (1 << CS53L30_IN3M_BIAS_SHIFT)
#define CS53L30_IN3M_BIAS_VCM (2 << CS53L30_IN3M_BIAS_SHIFT)
#define CS53L30_IN3P_BIAS_SHIFT 0
#define CS53L30_IN3P_BIAS_WIDTH 2
#define CS53L30_IN3P_BIAS_MASK (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT)
#define CS53L30_IN3P_BIAS_OPEN (0 << CS53L30_IN3P_BIAS_SHIFT)
#define CS53L30_IN3P_BIAS_PULL_DOWN (1 << CS53L30_IN3P_BIAS_SHIFT)
#define CS53L30_IN3P_BIAS_VCM (2 << CS53L30_IN3P_BIAS_SHIFT)
#define CS53L30_INBIAS_CTL1_DEFAULT (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\
CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM)
/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */
#define CS53L30_IN2M_BIAS_SHIFT 6
#define CS53L30_IN2M_BIAS_WIDTH 2
#define CS53L30_IN2M_BIAS_MASK (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT)
#define CS53L30_IN2M_BIAS_OPEN (0 << CS53L30_IN2M_BIAS_SHIFT)
#define CS53L30_IN2M_BIAS_PULL_DOWN (1 << CS53L30_IN2M_BIAS_SHIFT)
#define CS53L30_IN2M_BIAS_VCM (2 << CS53L30_IN2M_BIAS_SHIFT)
#define CS53L30_IN2P_BIAS_SHIFT 4
#define CS53L30_IN2P_BIAS_WIDTH 2
#define CS53L30_IN2P_BIAS_MASK (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT)
#define CS53L30_IN2P_BIAS_OPEN (0 << CS53L30_IN2P_BIAS_SHIFT)
#define CS53L30_IN2P_BIAS_PULL_DOWN (1 << CS53L30_IN2P_BIAS_SHIFT)
#define CS53L30_IN2P_BIAS_VCM (2 << CS53L30_IN2P_BIAS_SHIFT)
#define CS53L30_IN1M_BIAS_SHIFT 2
#define CS53L30_IN1M_BIAS_WIDTH 2
#define CS53L30_IN1M_BIAS_MASK (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT)
#define CS53L30_IN1M_BIAS_OPEN (0 << CS53L30_IN1M_BIAS_SHIFT)
#define CS53L30_IN1M_BIAS_PULL_DOWN (1 << CS53L30_IN1M_BIAS_SHIFT)
#define CS53L30_IN1M_BIAS_VCM (2 << CS53L30_IN1M_BIAS_SHIFT)
#define CS53L30_IN1P_BIAS_SHIFT 0
#define CS53L30_IN1P_BIAS_WIDTH 2
#define CS53L30_IN1P_BIAS_MASK (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT)
#define CS53L30_IN1P_BIAS_OPEN (0 << CS53L30_IN1P_BIAS_SHIFT)
#define CS53L30_IN1P_BIAS_PULL_DOWN (1 << CS53L30_IN1P_BIAS_SHIFT)
#define CS53L30_IN1P_BIAS_VCM (2 << CS53L30_IN1P_BIAS_SHIFT)
#define CS53L30_INBIAS_CTL2_DEFAULT (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\
CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM)
/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */
#define CS53L30_DMICx_STEREO_ENB_SHIFT 5
#define CS53L30_DMICx_STEREO_ENB_MASK (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
#define CS53L30_DMICx_STEREO_ENB (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
/* 0x88 and 0xCC are reserved bits */
#define CS53L30_DMIC1_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0x88)
#define CS53L30_DMIC2_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0xCC)
/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */
#define CS53L30_ADCxB_PDN_SHIFT 7
#define CS53L30_ADCxB_PDN_MASK (1 << CS53L30_ADCxB_PDN_SHIFT)
#define CS53L30_ADCxB_PDN (1 << CS53L30_ADCxB_PDN_SHIFT)
#define CS53L30_ADCxA_PDN_SHIFT 6
#define CS53L30_ADCxA_PDN_MASK (1 << CS53L30_ADCxA_PDN_SHIFT)
#define CS53L30_ADCxA_PDN (1 << CS53L30_ADCxA_PDN_SHIFT)
#define CS53L30_DMICx_PDN_SHIFT 2
#define CS53L30_DMICx_PDN_MASK (1 << CS53L30_DMICx_PDN_SHIFT)
#define CS53L30_DMICx_PDN (1 << CS53L30_DMICx_PDN_SHIFT)
#define CS53L30_DMICx_SCLK_DIV_SHIFT 1
#define CS53L30_DMICx_SCLK_DIV_MASK (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
#define CS53L30_DMICx_SCLK_DIV (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
#define CS53L30_CH_TYPE_SHIFT 0
#define CS53L30_CH_TYPE_MASK (1 << CS53L30_CH_TYPE_SHIFT)
#define CS53L30_CH_TYPE (1 << CS53L30_CH_TYPE_SHIFT)
#define CS53L30_ADCDMICx_PDN_MASK 0xFF
#define CS53L30_ADCDMICx_CTL1_DEFAULT (CS53L30_DMICx_PDN)
/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */
#define CS53L30_ADCx_NOTCH_DIS_SHIFT 7
#define CS53L30_ADCx_NOTCH_DIS_MASK (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
#define CS53L30_ADCx_NOTCH_DIS (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
#define CS53L30_ADCxB_INV_SHIFT 5
#define CS53L30_ADCxB_INV_MASK (1 << CS53L30_ADCxB_INV_SHIFT)
#define CS53L30_ADCxB_INV (1 << CS53L30_ADCxB_INV_SHIFT)
#define CS53L30_ADCxA_INV_SHIFT 4
#define CS53L30_ADCxA_INV_MASK (1 << CS53L30_ADCxA_INV_SHIFT)
#define CS53L30_ADCxA_INV (1 << CS53L30_ADCxA_INV_SHIFT)
#define CS53L30_ADCxB_DIG_BOOST_SHIFT 1
#define CS53L30_ADCxB_DIG_BOOST_MASK (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
#define CS53L30_ADCxB_DIG_BOOST (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
#define CS53L30_ADCxA_DIG_BOOST_SHIFT 0
#define CS53L30_ADCxA_DIG_BOOST_MASK (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
#define CS53L30_ADCxA_DIG_BOOST (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
#define CS53L30_ADCDMIC1_CTL2_DEFAULT (0)
/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */
#define CS53L30_ADCx_HPF_EN_SHIFT 3
#define CS53L30_ADCx_HPF_EN_MASK (1 << CS53L30_ADCx_HPF_EN_SHIFT)
#define CS53L30_ADCx_HPF_EN (1 << CS53L30_ADCx_HPF_EN_SHIFT)
#define CS53L30_ADCx_HPF_CF_SHIFT 1
#define CS53L30_ADCx_HPF_CF_WIDTH 2
#define CS53L30_ADCx_HPF_CF_MASK (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT)
#define CS53L30_ADCx_HPF_CF_1HZ86 (0 << CS53L30_ADCx_HPF_CF_SHIFT)
#define CS53L30_ADCx_HPF_CF_120HZ (1 << CS53L30_ADCx_HPF_CF_SHIFT)
#define CS53L30_ADCx_HPF_CF_235HZ (2 << CS53L30_ADCx_HPF_CF_SHIFT)
#define CS53L30_ADCx_HPF_CF_466HZ (3 << CS53L30_ADCx_HPF_CF_SHIFT)
#define CS53L30_ADCx_NG_ALL_SHIFT 0
#define CS53L30_ADCx_NG_ALL_MASK (1 << CS53L30_ADCx_NG_ALL_SHIFT)
#define CS53L30_ADCx_NG_ALL (1 << CS53L30_ADCx_NG_ALL_SHIFT)
#define CS53L30_ADCx_CTL3_DEFAULT (CS53L30_ADCx_HPF_EN)
/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */
#define CS53L30_ADCxB_NG_SHIFT 7
#define CS53L30_ADCxB_NG_MASK (1 << CS53L30_ADCxB_NG_SHIFT)
#define CS53L30_ADCxB_NG (1 << CS53L30_ADCxB_NG_SHIFT)
#define CS53L30_ADCxA_NG_SHIFT 6
#define CS53L30_ADCxA_NG_MASK (1 << CS53L30_ADCxA_NG_SHIFT)
#define CS53L30_ADCxA_NG (1 << CS53L30_ADCxA_NG_SHIFT)
#define CS53L30_ADCx_NG_BOOST_SHIFT 5
#define CS53L30_ADCx_NG_BOOST_MASK (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
#define CS53L30_ADCx_NG_BOOST (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
#define CS53L30_ADCx_NG_THRESH_SHIFT 2
#define CS53L30_ADCx_NG_THRESH_WIDTH 3
#define CS53L30_ADCx_NG_THRESH_MASK (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT)
#define CS53L30_ADCx_NG_DELAY_SHIFT 0
#define CS53L30_ADCx_NG_DELAY_WIDTH 2
#define CS53L30_ADCx_NG_DELAY_MASK (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT)
#define CS53L30_ADCx_NG_CTL_DEFAULT (0)
/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */
#define CS53L30_ADCxy_PREAMP_SHIFT 6
#define CS53L30_ADCxy_PREAMP_WIDTH 2
#define CS53L30_ADCxy_PREAMP_MASK (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT)
#define CS53L30_ADCxy_PGA_VOL_SHIFT 0
#define CS53L30_ADCxy_PGA_VOL_WIDTH 6
#define CS53L30_ADCxy_PGA_VOL_MASK (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT)
#define CS53L30_ADCxy_AFE_CTL_DEFAULT (0)
/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */
#define CS53L30_ADCxy_VOL_MUTE (0x80)
#define CS53L30_ADCxy_DIG_VOL_DEFAULT (0x0)
/* CS53L30_INT */
#define CS53L30_PDN_DONE (1 << 7)
#define CS53L30_THMS_TRIP (1 << 6)
#define CS53L30_SYNC_DONE (1 << 5)
#define CS53L30_ADC2B_OVFL (1 << 4)
#define CS53L30_ADC2A_OVFL (1 << 3)
#define CS53L30_ADC1B_OVFL (1 << 2)
#define CS53L30_ADC1A_OVFL (1 << 1)
#define CS53L30_MUTE_PIN (1 << 0)
#define CS53L30_DEVICE_INT_MASK 0xFF
#endif /* __CS53L30_H__ */
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of_device.h> #include <linux/i2c.h>
#include <linux/of_irq.h> #include <linux/property.h>
#include <linux/pm_wakeirq.h> #include <linux/pm_wakeirq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -382,11 +382,11 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data) ...@@ -382,11 +382,11 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
} }
/* /*
* DT to pdata conversion * DT/ACPI to pdata conversion
*/ */
static enum da7219_aad_micbias_pulse_lvl static enum da7219_aad_micbias_pulse_lvl
da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 2800: case 2800:
...@@ -400,7 +400,7 @@ static enum da7219_aad_micbias_pulse_lvl ...@@ -400,7 +400,7 @@ static enum da7219_aad_micbias_pulse_lvl
} }
static enum da7219_aad_btn_cfg static enum da7219_aad_btn_cfg
da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_btn_cfg(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 2: case 2:
...@@ -424,7 +424,7 @@ static enum da7219_aad_btn_cfg ...@@ -424,7 +424,7 @@ static enum da7219_aad_btn_cfg
} }
static enum da7219_aad_mic_det_thr static enum da7219_aad_mic_det_thr
da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_mic_det_thr(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 200: case 200:
...@@ -442,7 +442,7 @@ static enum da7219_aad_mic_det_thr ...@@ -442,7 +442,7 @@ static enum da7219_aad_mic_det_thr
} }
static enum da7219_aad_jack_ins_deb static enum da7219_aad_jack_ins_deb
da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 5: case 5:
...@@ -468,7 +468,7 @@ static enum da7219_aad_jack_ins_deb ...@@ -468,7 +468,7 @@ static enum da7219_aad_jack_ins_deb
} }
static enum da7219_aad_jack_det_rate static enum da7219_aad_jack_det_rate
da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str) da7219_aad_fw_jack_det_rate(struct snd_soc_codec *codec, const char *str)
{ {
if (!strcmp(str, "32ms_64ms")) { if (!strcmp(str, "32ms_64ms")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS; return DA7219_AAD_JACK_DET_RATE_32_64MS;
...@@ -485,7 +485,7 @@ static enum da7219_aad_jack_det_rate ...@@ -485,7 +485,7 @@ static enum da7219_aad_jack_det_rate
} }
static enum da7219_aad_jack_rem_deb static enum da7219_aad_jack_rem_deb
da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 1: case 1:
...@@ -503,7 +503,7 @@ static enum da7219_aad_jack_rem_deb ...@@ -503,7 +503,7 @@ static enum da7219_aad_jack_rem_deb
} }
static enum da7219_aad_btn_avg static enum da7219_aad_btn_avg
da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_btn_avg(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 1: case 1:
...@@ -521,7 +521,7 @@ static enum da7219_aad_btn_avg ...@@ -521,7 +521,7 @@ static enum da7219_aad_btn_avg
} }
static enum da7219_aad_adc_1bit_rpt static enum da7219_aad_adc_1bit_rpt
da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val) da7219_aad_fw_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
{ {
switch (val) { switch (val) {
case 1: case 1:
...@@ -538,97 +538,96 @@ static enum da7219_aad_adc_1bit_rpt ...@@ -538,97 +538,96 @@ static enum da7219_aad_adc_1bit_rpt
} }
} }
static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec) static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *codec)
{ {
struct device_node *np = codec->dev->of_node; struct device *dev = codec->dev;
struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad"); struct i2c_client *i2c = to_i2c_client(dev);
struct fwnode_handle *aad_np;
struct da7219_aad_pdata *aad_pdata; struct da7219_aad_pdata *aad_pdata;
const char *of_str; const char *fw_str;
u32 of_val32; u32 fw_val32;
aad_np = device_get_named_child_node(dev, "da7219_aad");
if (!aad_np) if (!aad_np)
return NULL; return NULL;
aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL); aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
if (!aad_pdata) if (!aad_pdata)
goto out; return NULL;
aad_pdata->irq = irq_of_parse_and_map(np, 0); aad_pdata->irq = i2c->irq;
if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
&of_val32) >= 0) &fw_val32) >= 0)
aad_pdata->micbias_pulse_lvl = aad_pdata->micbias_pulse_lvl =
da7219_aad_of_micbias_pulse_lvl(codec, of_val32); da7219_aad_fw_micbias_pulse_lvl(codec, fw_val32);
else else
aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time", if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
&of_val32) >= 0) &fw_val32) >= 0)
aad_pdata->micbias_pulse_time = of_val32; aad_pdata->micbias_pulse_time = fw_val32;
if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32); aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(codec, fw_val32);
else else
aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
aad_pdata->mic_det_thr = aad_pdata->mic_det_thr =
da7219_aad_of_mic_det_thr(codec, of_val32); da7219_aad_fw_mic_det_thr(codec, fw_val32);
else else
aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS; aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
aad_pdata->jack_ins_deb = aad_pdata->jack_ins_deb =
da7219_aad_of_jack_ins_deb(codec, of_val32); da7219_aad_fw_jack_ins_deb(codec, fw_val32);
else else
aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str)) if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
aad_pdata->jack_det_rate = aad_pdata->jack_det_rate =
da7219_aad_of_jack_det_rate(codec, of_str); da7219_aad_fw_jack_det_rate(codec, fw_str);
else else
aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
aad_pdata->jack_rem_deb = aad_pdata->jack_rem_deb =
da7219_aad_of_jack_rem_deb(codec, of_val32); da7219_aad_fw_jack_rem_deb(codec, fw_val32);
else else
aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
aad_pdata->a_d_btn_thr = (u8) of_val32; aad_pdata->a_d_btn_thr = (u8) fw_val32;
else else
aad_pdata->a_d_btn_thr = 0xA; aad_pdata->a_d_btn_thr = 0xA;
if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
aad_pdata->d_b_btn_thr = (u8) of_val32; aad_pdata->d_b_btn_thr = (u8) fw_val32;
else else
aad_pdata->d_b_btn_thr = 0x16; aad_pdata->d_b_btn_thr = 0x16;
if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
aad_pdata->b_c_btn_thr = (u8) of_val32; aad_pdata->b_c_btn_thr = (u8) fw_val32;
else else
aad_pdata->b_c_btn_thr = 0x21; aad_pdata->b_c_btn_thr = 0x21;
if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
aad_pdata->c_mic_btn_thr = (u8) of_val32; aad_pdata->c_mic_btn_thr = (u8) fw_val32;
else else
aad_pdata->c_mic_btn_thr = 0x3E; aad_pdata->c_mic_btn_thr = 0x3E;
if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32); aad_pdata->btn_avg = da7219_aad_fw_btn_avg(codec, fw_val32);
else else
aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0) if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
aad_pdata->adc_1bit_rpt = aad_pdata->adc_1bit_rpt =
da7219_aad_of_adc_1bit_rpt(codec, of_val32); da7219_aad_fw_adc_1bit_rpt(codec, fw_val32);
else else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
out:
of_node_put(aad_np);
return aad_pdata; return aad_pdata;
} }
...@@ -769,9 +768,9 @@ int da7219_aad_init(struct snd_soc_codec *codec) ...@@ -769,9 +768,9 @@ int da7219_aad_init(struct snd_soc_codec *codec)
da7219->aad = da7219_aad; da7219->aad = da7219_aad;
da7219_aad->codec = codec; da7219_aad->codec = codec;
/* Handle any DT/platform data */ /* Handle any DT/ACPI/platform data */
if ((codec->dev->of_node) && (da7219->pdata)) if (da7219->pdata && !da7219->pdata->aad_pdata)
da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec); da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
da7219_aad_handle_pdata(codec); da7219_aad_handle_pdata(codec);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/property.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm.h> #include <linux/pm.h>
...@@ -1418,7 +1419,7 @@ static struct snd_soc_dai_driver da7219_dai = { ...@@ -1418,7 +1419,7 @@ static struct snd_soc_dai_driver da7219_dai = {
/* /*
* DT * DT/ACPI
*/ */
static const struct of_device_id da7219_of_match[] = { static const struct of_device_id da7219_of_match[] = {
...@@ -1434,7 +1435,7 @@ static const struct acpi_device_id da7219_acpi_match[] = { ...@@ -1434,7 +1435,7 @@ static const struct acpi_device_id da7219_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, da7219_acpi_match); MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
static enum da7219_micbias_voltage static enum da7219_micbias_voltage
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) da7219_fw_micbias_lvl(struct device *dev, u32 val)
{ {
switch (val) { switch (val) {
case 1600: case 1600:
...@@ -1450,13 +1451,13 @@ static enum da7219_micbias_voltage ...@@ -1450,13 +1451,13 @@ static enum da7219_micbias_voltage
case 2600: case 2600:
return DA7219_MICBIAS_2_6V; return DA7219_MICBIAS_2_6V;
default: default:
dev_warn(codec->dev, "Invalid micbias level"); dev_warn(dev, "Invalid micbias level");
return DA7219_MICBIAS_2_2V; return DA7219_MICBIAS_2_2V;
} }
} }
static enum da7219_mic_amp_in_sel static enum da7219_mic_amp_in_sel
da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str) da7219_fw_mic_amp_in_sel(struct device *dev, const char *str)
{ {
if (!strcmp(str, "diff")) { if (!strcmp(str, "diff")) {
return DA7219_MIC_AMP_IN_SEL_DIFF; return DA7219_MIC_AMP_IN_SEL_DIFF;
...@@ -1465,29 +1466,29 @@ static enum da7219_mic_amp_in_sel ...@@ -1465,29 +1466,29 @@ static enum da7219_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) { } else if (!strcmp(str, "se_n")) {
return DA7219_MIC_AMP_IN_SEL_SE_N; return DA7219_MIC_AMP_IN_SEL_SE_N;
} else { } else {
dev_warn(codec->dev, "Invalid mic input type selection"); dev_warn(dev, "Invalid mic input type selection");
return DA7219_MIC_AMP_IN_SEL_DIFF; return DA7219_MIC_AMP_IN_SEL_DIFF;
} }
} }
static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec) static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
{ {
struct device_node *np = codec->dev->of_node; struct device *dev = codec->dev;
struct da7219_pdata *pdata; struct da7219_pdata *pdata;
const char *of_str; const char *of_str;
u32 of_val32; u32 of_val32;
pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return NULL; return NULL;
if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0) if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32); pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
else else
pdata->micbias_lvl = DA7219_MICBIAS_2_2V; pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str)) if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str))
pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str); pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str);
else else
pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF; pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
...@@ -1662,11 +1663,10 @@ static int da7219_probe(struct snd_soc_codec *codec) ...@@ -1662,11 +1663,10 @@ static int da7219_probe(struct snd_soc_codec *codec)
break; break;
} }
/* Handle DT/Platform data */ /* Handle DT/ACPI/Platform data */
if (codec->dev->of_node) da7219->pdata = dev_get_platdata(codec->dev);
da7219->pdata = da7219_of_to_pdata(codec); if (!da7219->pdata)
else da7219->pdata = da7219_fw_to_pdata(codec);
da7219->pdata = dev_get_platdata(codec->dev);
da7219_handle_pdata(codec); da7219_handle_pdata(codec);
......
...@@ -1599,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( ...@@ -1599,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
return pdata; return pdata;
} else if (match) { } else if (match) {
pdata = (struct davinci_mcasp_pdata*) match->data; pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev,
"Failed to allocate memory for pdata\n");
ret = -ENOMEM;
return pdata;
}
} else { } else {
/* control shouldn't reach here. something is wrong */ /* control shouldn't reach here. something is wrong */
ret = -EINVAL; ret = -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment