Skip to content

Commit

Permalink
hog
Browse files Browse the repository at this point in the history
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  • Loading branch information
morimoto committed Jan 25, 2022
1 parent d0c6bf3 commit 017a564
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 25 deletions.
3 changes: 2 additions & 1 deletion arch/arm64/boot/dts/renesas/ulcb.dtsi
Expand Up @@ -222,7 +222,7 @@
asahi-kasei,out4-single-end;
asahi-kasei,out5-single-end;
asahi-kasei,out6-single-end;

asahi-kasei,tdm256;
port {
ak4613_endpoint: endpoint {
remote-endpoint = <&rsnd_for_ak4613>;
Expand Down Expand Up @@ -410,6 +410,7 @@
remote-endpoint = <&ak4613_endpoint>;
bitclock-master;
frame-master;
dai-tdm-slot-num = <8>;
playback = <&ssi0>, <&src0>, <&dvc0>;
capture = <&ssi1>, <&src1>, <&dvc1>;
};
Expand Down
220 changes: 196 additions & 24 deletions sound/soc/codecs/ak4613.c
Expand Up @@ -10,11 +10,45 @@
// Based on ak4535.c by Richard Purdie
// Based on wm8753.c by Liam Girdwood

/*
* (*) not used
* +------------+
* SDTI1 -> | | -> SDTO1
* SDTI2 -> | AK4613 | -> SDTO2 (*)
* SDTI3 -> | |
* (*) SDTI4 -> | |
* (*) SDTI5 -> | |
* (*) SDTI6 -> | |
* +------------+
*
* (o) tested
*
* [STEREO mode]
* (o) Playback 2ch : SDTI1
* (o) Capture 2ch : SDTO1
*
* [TDM512 mode]
* Playback 12ch : SDTI1
* Capture 4ch : SDTO1
*
* [TDM256 mode]
* Playback 12ch : SDTI1 + SDTI2
* (o) Playback 8ch : SDTI1
* (o) Capture 4ch : SDTO1
*
* [TDM128 mode]
* Playback 12ch : SDTI1 + SDTI2 + SDTI3
* Playback 8ch : SDTI1 + SDTI2
* Playback 4ch : SDTI1
* Capture 4ch : SDTO1
*/

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
Expand Down Expand Up @@ -79,24 +113,64 @@
#define OCTRL_MASK (0x3F)

struct ak4613_interface {
unsigned int mode;
unsigned int width;
unsigned int fmt;
u8 ctrl1;
};

/*
* configs
*
* 0x000000BA
*
* A : AK4613_CONFIG_MODE_x
* B : AK4613_CONFIG_SDTI_x
*/
#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x
#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK)

/*
* AK4613_CONFIG_SDTI_x
*
* It indicates how many SDTIx is connected.
*/
#define AK4613_CONFIG_SDTI_MASK (0xF << 4)
#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4)
#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x))
#define AK4613_CONFIG_SDTI_num(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF)

/*
* AK4613_CONFIG_MODE_x
*
* Same as Ctrl1 :: TDM1/TDM0
* No shift is requested
* see
* AK4613_INTERFACE_MODE()
* AK4613_CONFIG_GET()
* Table 11/12/13/14
*/
#define AK4613_CONFIG_MODE_MASK (0xF)
#define AK4613_CONFIG_MODE_STEREO (0x0)
#define AK4613_CONFIG_MODE_TDM512 (0x1)
#define AK4613_CONFIG_MODE_TDM256 (0x2)
#define AK4613_CONFIG_MODE_TDM128 (0x3)

struct ak4613_priv {
struct mutex lock;
const struct ak4613_interface *iface;
struct snd_pcm_hw_constraint_list constraint;
struct snd_pcm_hw_constraint_list constraint_rates;
struct snd_pcm_hw_constraint_list constraint_channels;
struct work_struct dummy_write_work;
struct snd_soc_component *component;
unsigned int rate;
unsigned int sysclk;

unsigned int fmt;
unsigned int configs;
int cnt;
u8 oc;
u8 ic;
int cnt;
};

/*
Expand Down Expand Up @@ -134,24 +208,27 @@ static const struct reg_default ak4613_reg[] = {
};

/*
* CTRL1 register
* Table 11 (Stereo mode)
* CTRL1 register
* see
* Table 11/12/13/14
*/
#define AUDIO_IFACE(_val, _width, _fmt) \
#define AUDIO_IFACE(_tdm, _dif, _width, _fmt) \
{ \
.ctrl1 = (_val << 3), \
.ctrl1 = (AK4613_CONFIG_MODE_##_tdm << 6) | (_dif << 3), \
.width = _width, \
.fmt = SND_SOC_DAIFMT_##_fmt,\
}
static const struct ak4613_interface ak4613_iface[] = {
/* It doesn't support asymmetric format so far. */

/*
* Stereo mode
*/
AUDIO_IFACE(0x03, 24, LEFT_J),
AUDIO_IFACE(0x04, 24, I2S),
AUDIO_IFACE(STEREO, 0x3, 24, LEFT_J),
AUDIO_IFACE(STEREO, 0x4, 24, I2S),

AUDIO_IFACE(TDM256, 0x3, 24, LEFT_J),
AUDIO_IFACE(TDM256, 0x4, 24, I2S),
};
/* convert iface to AK4613_FLAG_MODE_x */
#define AK4613_INTERFACE_MODE(priv) (((priv)->iface->ctrl1 >> 6) & 0x3)

static const struct regmap_config ak4613_regmap_cfg = {
.reg_bits = 8,
Expand Down Expand Up @@ -261,9 +338,11 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
mutex_unlock(&priv->lock);
}

static void ak4613_hw_constraints(struct ak4613_priv *priv,
struct snd_pcm_runtime *runtime)
static void ak4613_hw_constraints(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *runtime = substream->runtime;
static const unsigned int ak4613_rates[] = {
32000,
44100,
Expand All @@ -274,10 +353,24 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
176400,
192000,
};
struct snd_pcm_hw_constraint_list *constraint = &priv->constraint;
#define AK4613_CHANNEL_2 0
#define AK4613_CHANNEL_4 1
#define AK4613_CHANNEL_8 2
#define AK4613_CHANNEL_12 3
static const unsigned int ak4613_channels[] = {
[AK4613_CHANNEL_2] = 2,
[AK4613_CHANNEL_4] = 4,
[AK4613_CHANNEL_8] = 8,
[AK4613_CHANNEL_12] = 12,
};
struct snd_pcm_hw_constraint_list *constraint;
unsigned int mask;
unsigned int mode;
unsigned int fs;
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int i;

constraint = &priv->constraint_rates;
constraint->list = ak4613_rates;
constraint->mask = 0;
constraint->count = 0;
Expand All @@ -292,7 +385,7 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,
* Normal: [32kHz, 48kHz] : 256fs or 512fs
* Double: [64kHz, 96kHz] : 256fs
* Quad : [128kHz,192kHz]: 128fs
*/
*/
for (i = 0; i < ARRAY_SIZE(ak4613_rates); i++) {
/* minimum fs on each range */
fs = (ak4613_rates[i] <= 96000) ? 256 : 128;
Expand All @@ -303,6 +396,75 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv,

snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, constraint);

mode = 0;
if (AK4613_CONFIG_GET(priv, MODE) == AK4613_CONFIG_MODE_STEREO) {
/*
* If board supports STEREO only
*/
mode = mask = (1 << AK4613_CHANNEL_2);
} else {
/*
* If board supports TDM
*/
unsigned int mode = 0;
int sdti_num = AK4613_CONFIG_SDTI_num(priv);

if (priv->cnt) {
/*
* If it was already working,
* the constraint is same as working setting.
*/
mode = AK4613_INTERFACE_MODE(priv);
mask = 0; /* no default */
} else {
/*
* It is not yet working,
* the constraint is based on board configs.
*/
mode = AK4613_CONFIG_GET(priv, MODE);
mask = (1 << AK4613_CHANNEL_2); /* STEREO is default */
}

/* FIXME: not yet full support */
switch(mode) {
case AK4613_CONFIG_MODE_STEREO:
mask |= (1 << AK4613_CHANNEL_2);
break;
case AK4613_CONFIG_MODE_TDM256:
if (is_play) {
switch (sdti_num) {
case 1:
mask |= (1 << AK4613_CHANNEL_8);
break;
case 2:
/* NOT TESTED */
mask |= (1 << AK4613_CHANNEL_12);
WARN_ON(1);
break;
default:
BUG();
break;
}
} else {
mask |= (1 << AK4613_CHANNEL_4);
}
break;
/* NOT SUPPORTED */
case AK4613_CONFIG_MODE_TDM512:
case AK4613_CONFIG_MODE_TDM128:
default:
BUG();
}
}

constraint = &priv->constraint_channels;

constraint->list = ak4613_channels;
constraint->mask = mask;
constraint->count = sizeof(ak4613_channels);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
}

static int ak4613_dai_startup(struct snd_pcm_substream *substream,
Expand All @@ -313,9 +475,10 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream,

mutex_lock(&priv->lock);
priv->cnt++;
mutex_unlock(&priv->lock);

ak4613_hw_constraints(priv, substream->runtime);
printk("----startup\n");
ak4613_hw_constraints(component, substream);
mutex_unlock(&priv->lock);

return 0;
}
Expand Down Expand Up @@ -408,14 +571,10 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
}
priv->rate = rate;

/*
* FIXME
*
* It doesn't support TDM at this point
*/
ret = -EINVAL;
iface = NULL;

printk("----hw param\n");
mutex_lock(&priv->lock);
if (priv->iface) {
if (ak4613_dai_fmt_matching(priv->iface, fmt, width))
Expand Down Expand Up @@ -584,14 +743,14 @@ static struct snd_soc_dai_driver ak4613_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.channels_max = 8, /* It supports TDM256 with SDTI1 (8ch) only */
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.channels_max = 4,
.rates = AK4613_PCM_RATE,
.formats = AK4613_PCM_FMTBIT,
},
Expand Down Expand Up @@ -651,6 +810,19 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
if (!of_get_property(np, prop, NULL))
priv->oc |= 1 << i;
}

/*
* TDM mode
*/
/* FIXME: not yet full support */
snprintf(prop, sizeof(prop), "asahi-kasei,tdm256");
if (of_get_property(np, prop, NULL))
AK4613_CONFIG_SET(priv, MODE_TDM256);

/*
* connected STDI
*/
AK4613_CONFIG_SDTI_set(priv, of_graph_get_endpoint_count(np));
}

static int ak4613_i2c_probe(struct i2c_client *i2c,
Expand Down

0 comments on commit 017a564

Please sign in to comment.