blob: 7f65bfa1c8c20f55a140ffd8f9cbfaafbe223895 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <sound/soc.h>
#include "tdm_hw.h"
#include "iomap.h"
#include "tdm_gain_version.h"
#define MST_CLK_INVERT_PH0_PAD_BCLK BIT(0)
#define MST_CLK_INVERT_PH0_PAD_FCLK BIT(1)
#define MST_CLK_INVERT_PH1_TDMIN_BCLK BIT(2)
#define MST_CLK_INVERT_PH1_TDMIN_FCLK BIT(3)
#define MST_CLK_INVERT_PH2_TDMOUT_BCLK BIT(4)
#define MST_CLK_INVERT_PH2_TDMOUT_FCLK BIT(5)
/* without audio handler, it should be improved */
void aml_tdm_enable(struct aml_audio_controller *actrl,
int stream, int index,
bool is_enable)
{
unsigned int offset, reg;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
pr_debug("tdm playback enable\n");
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL0
- EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL0;
}
aml_audiobus_update_bits(actrl, reg, 1 << 31, is_enable << 31);
} else {
pr_debug("tdm capture enable\n");
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL
- EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg, 1 << 31 | 1 << 26, is_enable << 31 | 1 << 26);
}
}
void aml_tdm_arb_config(struct aml_audio_controller *actrl)
{
/* config ddr arb */
aml_audiobus_write(actrl, EE_AUDIO_ARB_CTRL, 1 << 31 | 0xff << 0);
}
void aml_tdm_fifo_reset(struct aml_audio_controller *actrl,
int stream, int index)
{
unsigned int reg, offset;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL0
- EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL0;
}
/* reset afifo */
aml_audiobus_update_bits(actrl, reg, 3 << 28, 0);
aml_audiobus_update_bits(actrl, reg, 1 << 29, 1 << 29);
aml_audiobus_update_bits(actrl, reg, 1 << 28, 1 << 28);
} else {
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL
- EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
/* reset afifo */
aml_audiobus_update_bits(actrl, reg, 3 << 28, 0);
aml_audiobus_update_bits(actrl, reg, 1 << 29, 1 << 29);
aml_audiobus_update_bits(actrl, reg, 1 << 28, 1 << 28);
}
}
void tdm_enable(int tdm_index, int is_enable)
{
unsigned int offset, reg;
if (tdm_index < 3) {
pr_info("tdmout is_enable:%d\n", is_enable);
offset = EE_AUDIO_TDMOUT_B_CTRL0
- EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * tdm_index;
audiobus_update_bits(reg, 1 << 31, is_enable << 31);
} else if (tdm_index < 6) {
pr_info("tdmin is_enable:%d\n", is_enable);
tdm_index -= 3;
offset = EE_AUDIO_TDMIN_B_CTRL
- EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * tdm_index;
audiobus_update_bits(reg, 1 << 31, is_enable << 31);
}
}
void tdm_fifo_enable(int tdm_index, int is_enable)
{
unsigned int reg, offset;
if (tdm_index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL0
- EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * tdm_index;
if (is_enable) {
audiobus_update_bits(reg, 1 << 29, 1 << 29);
audiobus_update_bits(reg, 1 << 28, 1 << 28);
} else {
audiobus_update_bits(reg, 3 << 28, 0);
}
} else if (tdm_index < 6) {
tdm_index -= 3;
offset = EE_AUDIO_TDMIN_B_CTRL
- EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * tdm_index;
if (is_enable) {
audiobus_update_bits(reg, 1 << 29, 1 << 29);
audiobus_update_bits(reg, 1 << 28, 1 << 28);
} else {
audiobus_update_bits(reg, 3 << 28, 0);
}
}
}
int tdmout_get_frddr_type(int bitwidth)
{
unsigned int frddr_type = 0;
switch (bitwidth) {
case 8:
frddr_type = 0;
break;
case 16:
frddr_type = 2;
break;
case 24:
case 32:
frddr_type = 4;
break;
default:
pr_err("invalid bit_depth: %d\n", bitwidth);
break;
}
return frddr_type;
}
void aml_tdm_fifo_ctrl(struct aml_audio_controller *actrl,
int bitwidth, int stream,
int index, unsigned int fifo_id)
{
unsigned int frddr_type = tdmout_get_frddr_type(bitwidth);
unsigned int reg, offset;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
pr_debug("tdm prepare----playback\n");
// from ddr, 63bit split into 2 samples
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL1;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL1
- EE_AUDIO_TDMOUT_A_CTRL1;
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL1;
}
aml_audiobus_update_bits(actrl, reg,
0x3 << 24 | 0x1f << 8 | 0x7 << 4,
fifo_id << 24 | (bitwidth - 1) << 8 | frddr_type << 4);
} else {
pr_debug("tdm prepare----capture\n");
}
}
static void aml_clk_set_tdmout_by_id(struct aml_audio_controller *actrl,
unsigned int tdm_index,
unsigned int sclk_sel,
unsigned int lrclk_sel,
bool sclk_ws_inv,
bool is_master,
bool binv)
{
unsigned int val_sclk_ws_inv = 0;
unsigned int reg;
if (tdm_index == 3)
reg = EE_AUDIO_CLK_TDMOUT_D_CTRL;
else if (tdm_index < 3)
reg = EE_AUDIO_CLK_TDMOUT_A_CTRL + tdm_index;
else
reg = EE_AUDIO_CLK_TDMOUT_A_CTRL;
/* This is just a copy from previous setting. WHY??? */
val_sclk_ws_inv = sclk_ws_inv && is_master;
if (val_sclk_ws_inv)
aml_audiobus_update_bits(actrl, reg,
0x3 << 30 | 1 << 28 | 0xf << 24 | 0xf << 20,
0x3 << 30 | val_sclk_ws_inv << 28 |
sclk_sel << 24 | lrclk_sel << 20);
else
aml_audiobus_update_bits(actrl, reg,
0x3 << 30 | 1 << 29 | 0xf << 24 | 0xf << 20,
0x3 << 30 | binv << 29 |
sclk_sel << 24 | lrclk_sel << 20);
}
static void aml_clk_set_tdmin_by_id(struct aml_audio_controller *actrl,
unsigned int tdm_index,
unsigned int sclk_sel,
unsigned int lrclk_sel)
{
unsigned int reg;
if (tdm_index == 3) {
reg = EE_AUDIO_CLK_TDMIN_D_CTRL;
} else if (tdm_index < 3) {
reg = EE_AUDIO_CLK_TDMIN_A_CTRL + tdm_index;
} else {
reg = EE_AUDIO_CLK_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl,
reg,
0xff << 20,
sclk_sel << 24 | lrclk_sel << 20);
}
static void aml_tdmout_invert_lrclk(struct aml_audio_controller *actrl,
unsigned int tdm_index,
bool finv)
{
unsigned int reg_out;
unsigned int off_set = EE_AUDIO_TDMOUT_B_CTRL1 - EE_AUDIO_TDMOUT_A_CTRL1;
if (tdm_index == 3)
reg_out = EE_AUDIO_TDMOUT_D_CTRL1;
else if (tdm_index < 3)
reg_out = EE_AUDIO_TDMOUT_A_CTRL1 + off_set * tdm_index;
else
reg_out = EE_AUDIO_TDMOUT_A_CTRL1;
aml_audiobus_update_bits(actrl,
reg_out, 0x1 << 28, finv << 28);
}
static void aml_tdmout_bclk_skew(struct aml_audio_controller *actrl,
unsigned int tdm_index,
unsigned int bclkout_skew)
{
unsigned int reg_out;
unsigned int off_set = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
if (tdm_index == 3)
reg_out = EE_AUDIO_TDMOUT_D_CTRL0;
else if (tdm_index < 3)
reg_out = EE_AUDIO_TDMOUT_A_CTRL0 + off_set * tdm_index;
else
reg_out = EE_AUDIO_TDMOUT_A_CTRL0;
aml_audiobus_update_bits(actrl, reg_out, 0x1f << 15, bclkout_skew << 15);
}
void aml_tdm_set_format(struct aml_audio_controller *actrl,
struct pcm_setting *p_config,
unsigned int clk_sel,
unsigned int index,
unsigned int fmt,
unsigned int capture_active,
unsigned int playback_active,
bool tdmin_src_hdmirx)
{
unsigned int binv, finv, id;
unsigned int valb, valf;
unsigned int reg_in, reg_out, off_set;
int bclkin_skew, bclkout_skew;
int master_mode;
unsigned int clkctl = 0;
id = index;
binv = 0;
finv = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
valb = SLAVE_A + id;
valf = SLAVE_A + id;
master_mode = 0;
break;
case SND_SOC_DAIFMT_CBS_CFS:
valb = MASTER_A + clk_sel;
valf = MASTER_A + clk_sel;
master_mode = 1;
break;
default:
return;
}
/*
* if tdmin source is hdmirx
* the clock is slave and from hdmirx
* slv_sclk_f = HDMIRX_I2S_SCLK
*/
if (tdmin_src_hdmirx)
aml_clk_set_tdmin_by_id(actrl, id, 11, 11);
else
aml_clk_set_tdmin_by_id(actrl, id, valb, valf);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
if (p_config->sclk_ws_inv) {
if (master_mode)
bclkout_skew = 2;
else
bclkout_skew = 3;
} else {
bclkout_skew = 1;
}
bclkin_skew = 3;
clkctl |= MST_CLK_INVERT_PH0_PAD_FCLK
| MST_CLK_INVERT_PH2_TDMOUT_FCLK;
finv = 1;
if (master_mode) {
clkctl |= MST_CLK_INVERT_PH0_PAD_BCLK;
if (capture_active)
binv |= 1;
} else {
if (playback_active)
binv |= 1;
}
break;
case SND_SOC_DAIFMT_DSP_A:
/*
* Frame high, 1clk before data, one bit for frame sync,
* frame sync starts one serial clock cycle earlier,
* that is, together with the last bit of the previous
* data word.
*/
if (p_config->sclk_ws_inv) {
if (master_mode)
bclkout_skew = 2;
else
bclkout_skew = 3;
} else {
bclkout_skew = 1;
}
bclkin_skew = 3;
if (capture_active)
binv |= 1;
break;
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_DSP_B:
/*
* Frame high, one bit for frame sync,
* frame sync asserts with the first bit of the frame.
*/
if (p_config->sclk_ws_inv) {
if (master_mode)
bclkout_skew = 3;
else
bclkout_skew = 4;
} else {
bclkout_skew = 2;
}
bclkin_skew = 2;
if (capture_active)
binv |= 1;
break;
default:
return;
}
p_config->pcm_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
pr_debug("pad clk ctl value:%x\n", clkctl);
/* set lrclk/bclk invertion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
/* Invert both clocks */
if (!master_mode)
binv ^= 1;
finv |= 1;
clkctl ^= MST_CLK_INVERT_PH0_PAD_BCLK;
clkctl ^= MST_CLK_INVERT_PH0_PAD_FCLK;
break;
case SND_SOC_DAIFMT_IB_NF:
/* Invert bit clock */
if (!master_mode)
binv ^= 1;
clkctl ^= MST_CLK_INVERT_PH0_PAD_BCLK;
break;
case SND_SOC_DAIFMT_NB_IF:
/* Invert frame clock */
finv ^= 1;
clkctl ^= MST_CLK_INVERT_PH0_PAD_FCLK;
break;
case SND_SOC_DAIFMT_NB_NF:
/* normal cases */
break;
default:
return;
}
pr_debug("sclk_ph0 (pad) clk ctl set:%x\n", clkctl);
/* clk ctrl: delay line and invert clk */
/*clkctl |= 0x88880000;*/
if (master_mode) {
off_set = EE_AUDIO_MST_B_SCLK_CTRL1 - EE_AUDIO_MST_A_SCLK_CTRL1;
reg_out = EE_AUDIO_MST_A_SCLK_CTRL1 + off_set * id;
aml_audiobus_update_bits(actrl, reg_out, 0x3f, clkctl);
}
pr_info("master_mode(%d), binv(%d), finv(%d) out_skew(%d), in_skew(%d)\n",
master_mode, binv, finv, bclkout_skew, bclkin_skew);
/* TDM out */
if (playback_active) {
aml_clk_set_tdmout_by_id(actrl,
id, valb, valf,
p_config->sclk_ws_inv, master_mode, binv);
aml_tdmout_invert_lrclk(actrl, id, finv);
aml_tdmout_bclk_skew(actrl, id, bclkout_skew);
}
/* TDM in */
if (capture_active) {
int mode;
if (id == 3)
reg_in = EE_AUDIO_CLK_TDMIN_D_CTRL;
else if (id < 3)
reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL + id;
else
reg_in = EE_AUDIO_CLK_TDMIN_A_CTRL;
aml_audiobus_update_bits(actrl, reg_in,
0x3 << 30, 0x3 << 30);
if (master_mode)
aml_audiobus_update_bits(actrl, reg_in,
0x1 << 29, binv << 29);
if (id == 3) {
reg_in = EE_AUDIO_TDMIN_D_CTRL;
} else if (id < 3) {
off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * id;
} else {
reg_in = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg_in,
3 << 26 | 0x7 << 16, 3 << 26 | bclkin_skew << 16);
aml_audiobus_update_bits(actrl, reg_in,
0x1 << 25, finv << 25);
mode = (p_config->pcm_mode == SND_SOC_DAIFMT_I2S) ? 0x1 : 0x0;
aml_audiobus_update_bits(actrl, reg_in, 0x1 << 30, mode << 30);
}
}
void aml_update_tdmin_skew(struct aml_audio_controller *actrl,
int idx, int skew)
{
unsigned int reg_in, off_set;
if (idx == 3) {
reg_in = EE_AUDIO_TDMIN_D_CTRL;
} else if (idx < 3) {
off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * idx;
} else {
reg_in = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg_in,
0x7 << 16, skew << 16);
}
void aml_update_tdmin_rev_ws(struct aml_audio_controller *actrl,
int idx, int is_rev)
{
unsigned int reg_in, off_set;
if (idx == 3) {
reg_in = EE_AUDIO_TDMIN_D_CTRL;
} else if (idx < 3) {
off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * idx;
} else {
reg_in = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg_in, 0x1 << 25, is_rev << 25);
}
void aml_tdm_set_oe_v1(struct aml_audio_controller *actrl,
int index, int force_oe, int oe_val)
{
if (force_oe) {
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL0;
}
aml_audiobus_update_bits(actrl, reg, 0xf << 24, force_oe << 24);
/* force oe val, in or out */
if (index == 3)
reg = EE_AUDIO_TDMOUT_D_CTRL1;
else if (index < 3)
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index;
else
reg = EE_AUDIO_TDMOUT_A_CTRL1;
aml_audiobus_update_bits(actrl, reg, 0xf, oe_val);
}
}
void aml_tdm_set_oe_v2(struct aml_audio_controller *actrl,
int index, int force_oe, int oe_val)
{
if (force_oe) {
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL2;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL2 - EE_AUDIO_TDMOUT_A_CTRL2;
reg = EE_AUDIO_TDMOUT_A_CTRL2 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL2;
}
aml_audiobus_update_bits(actrl, reg, 0xff << 8, force_oe << 8);
/* force oe val, in or out */
if (oe_val) {
aml_audiobus_update_bits
(actrl, reg, 0xff << 16, oe_val << 16);
}
}
}
void aml_tdm_set_slot_out(struct aml_audio_controller *actrl,
int index, int slots, int slot_width)
{
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL0;
}
aml_audiobus_update_bits
(actrl, reg, 0x3ff, ((slots - 1) << 5) | (slot_width - 1));
}
void aml_tdm_set_slot_in(struct aml_audio_controller *actrl,
int index, int in_src, int slot_width)
{
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg,
0xf << 20 | 0x1f, in_src << 20 | (slot_width - 1));
}
void aml_update_tdmin_src(struct aml_audio_controller *actrl,
int index, int in_src)
{
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg, 0xf << 20, in_src << 20);
}
void tdmin_set_chnum_en(struct aml_audio_controller *actrl,
int index, bool enable)
{
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg,
0x1 << 6, enable << 6);
}
void aml_tdm_set_channel_mask(struct aml_audio_controller *actrl,
int stream, int index, int lane, int mask)
{
unsigned int offset, reg;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (lane >= LANE_MAX1) {
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_MASK4;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_MASK4
- EE_AUDIO_TDMOUT_A_MASK4;
reg = EE_AUDIO_TDMOUT_A_MASK4 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_MASK4;
}
lane -= LANE_MAX1;
} else {
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_MASK0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_MASK0
- EE_AUDIO_TDMOUT_A_MASK0;
reg = EE_AUDIO_TDMOUT_A_MASK0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_MASK0;
}
}
} else {
if (lane >= LANE_MAX1) {
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_MASK4;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_MASK4
- EE_AUDIO_TDMIN_A_MASK4;
reg = EE_AUDIO_TDMIN_A_MASK4 + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_MASK4;
}
lane -= LANE_MAX1;
} else {
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_MASK0;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_MASK0
- EE_AUDIO_TDMIN_A_MASK0;
reg = EE_AUDIO_TDMIN_A_MASK0 + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_MASK0;
}
}
}
aml_audiobus_write(actrl, reg + lane, mask);
}
void aml_tdm_set_lane_channel_swap(struct aml_audio_controller *actrl,
int stream, int index, int swap0, int swap1)
{
unsigned int offset, reg;
pr_debug("\t %s swap0 = %#x, swap1 = %#x\n",
(stream == SNDRV_PCM_STREAM_PLAYBACK) ? "tdmout" : "tdmin",
swap0, swap1);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_SWAP0;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_SWAP0
- EE_AUDIO_TDMOUT_A_SWAP0;
reg = EE_AUDIO_TDMOUT_A_SWAP0 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_SWAP0;
}
aml_audiobus_write(actrl, reg, swap0);
if (swap1) {
if (index == 3) {
reg = EE_AUDIO_TDMOUT_D_SWAP1;
} else if (index < 3) {
offset = EE_AUDIO_TDMOUT_B_SWAP1
- EE_AUDIO_TDMOUT_A_SWAP1;
reg = EE_AUDIO_TDMOUT_A_SWAP1 + offset * index;
} else {
reg = EE_AUDIO_TDMOUT_A_SWAP1;
}
aml_audiobus_write(actrl, reg, swap1);
}
} else {
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_SWAP0;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_SWAP0
- EE_AUDIO_TDMIN_A_SWAP0;
reg = EE_AUDIO_TDMIN_A_SWAP0 + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_SWAP0;
}
aml_audiobus_write(actrl, reg, swap0);
if (swap1) {
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_SWAP1;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_SWAP1
- EE_AUDIO_TDMIN_A_SWAP1;
reg = EE_AUDIO_TDMIN_A_SWAP1 + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_SWAP1;
}
aml_audiobus_write(actrl, reg, swap1);
}
}
}
void aml_tdm_set_bclk_ratio(struct aml_audio_controller *actrl,
int clk_sel, int lrclk_hi, int bclk_ratio)
{
unsigned int reg, reg_step = 2;
reg = EE_AUDIO_MST_A_SCLK_CTRL0 + reg_step * clk_sel;
aml_audiobus_update_bits(actrl, reg,
(3 << 30) | 0x3ff << 10 | 0x3ff,
(3 << 30) | lrclk_hi << 10 | bclk_ratio);
}
void aml_tdm_set_lrclkdiv(struct aml_audio_controller *actrl,
int clk_sel, int ratio)
{
unsigned int reg, reg_step = 2;
pr_debug("aml_dai_set_clkdiv, clksel(%d), ratio(%d)\n",
clk_sel, ratio);
reg = EE_AUDIO_MST_A_SCLK_CTRL0 + reg_step * clk_sel;
aml_audiobus_update_bits(actrl, reg,
(3 << 30) | (0x3ff << 20),
(3 << 30) | (ratio << 20));
}
void aml_tdmout_select_aed(bool enable, int tdmout_id)
{
unsigned int reg, offset;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL1;
} else if (tdmout_id < 3) {
/* select eq_drc output */
offset = EE_AUDIO_TDMOUT_B_CTRL1
- EE_AUDIO_TDMOUT_A_CTRL1;
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL1;
}
audiobus_update_bits(reg, 0x1 << 31, enable << 31);
}
void aml_tdmout_get_aed_info(int tdmout_id,
int *bitwidth, int *frddrtype)
{
unsigned int reg, offset, val;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_CTRL1;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_CTRL1
- EE_AUDIO_TDMOUT_A_CTRL1;
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_CTRL1;
}
val = audiobus_read(reg);
if (bitwidth)
*bitwidth = (val >> 8) & 0x1f;
if (frddrtype)
*frddrtype = (val >> 4) & 0x7;
}
void aml_tdmout_enable_gain(int tdmout_id, int en, int gain_ver)
{
unsigned int reg, offset;
switch (gain_ver) {
case GAIN_VER1:
offset = EE_AUDIO_TDMOUT_B_CTRL1
- EE_AUDIO_TDMOUT_A_CTRL1;
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * tdmout_id;
audiobus_update_bits(reg, 0x1 << 26, !!en << 26);
break;
case GAIN_VER2:
offset = EE_AUDIO_TDMOUT_B_CTRL1
- EE_AUDIO_TDMOUT_A_CTRL1;
reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * tdmout_id;
audiobus_update_bits(reg, 0x1 << 7, !!en << 7);
break;
case GAIN_VER3:
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_GAIN_EN;
} else {
offset = EE_AUDIO_TDMOUT_B_GAIN_EN - EE_AUDIO_TDMOUT_A_GAIN_EN;
reg = EE_AUDIO_TDMOUT_A_GAIN_EN + offset * tdmout_id;
}
if (en)
audiobus_update_bits(reg, 0xFF << 0, 0xFF << 0);
else
audiobus_update_bits(reg, 0xFF << 0, 0x0 << 0);
break;
}
}
void aml_tdm_mclk_pad_select(struct aml_audio_controller *actrl,
int mpad, int mpad_offset, int mclk_sel)
{
unsigned int reg, mask_offset, val_offset;
switch (mpad) {
case 0:
mask_offset = 0x7 << 0;
val_offset = mclk_sel << 0;
break;
case 1:
mask_offset = 0x7 << 4;
val_offset = mclk_sel << 4;
break;
default:
mask_offset = 0x7 << 4;
val_offset = 0;
pr_info("unknown tdm mpad:%d\n", mpad);
break;
}
reg = EE_AUDIO_MST_PAD_CTRL0(mpad_offset);
if (actrl)
aml_audiobus_update_bits(actrl, reg,
mask_offset, val_offset);
else
audiobus_update_bits(reg,
mask_offset, val_offset);
}
void aml_tdm_sclk_pad_select(struct aml_audio_controller *actrl,
int mpad_offset, int tdm_index, int clk_sel)
{
unsigned int reg, mask_offset, val_offset;
reg = EE_AUDIO_MST_PAD_CTRL1(mpad_offset);
switch (tdm_index) {
case 0:
mask_offset = 0x7 << 16 | 0x7 << 0;
val_offset = clk_sel << 16 | clk_sel << 0;
break;
case 1:
mask_offset = 0x7 << 20 | 0x7 << 4;
val_offset = clk_sel << 20 | clk_sel << 4;
break;
case 2:
mask_offset = 0x7 << 24 | 0x7 << 8;
val_offset = clk_sel << 24 | clk_sel << 8;
break;
default:
pr_err("unknown mclk pad, tdm index:%d\n", tdm_index);
return;
}
if (actrl)
aml_audiobus_update_bits(actrl, reg, mask_offset, val_offset);
else
audiobus_update_bits(reg, mask_offset, val_offset);
}
void i2s_to_hdmitx_ctrl(int i2s_tohdmitxen_separated, int tdm_index)
{
audiobus_write(EE_AUDIO_TOHDMITX_CTRL0,
tdm_index << 12 /* dat_sel */
| tdm_index << 8 /* lrclk_sel */
| 1 << 7 /* Bclk_cap_inv */
| 0 << 6 /* Bclk_o_inv */
| tdm_index << 4 /* Bclk_sel */
);
if (i2s_tohdmitxen_separated) {
/* if tohdmitx_en is separated, need do:
* step1: enable/disable clk
* step2: enable/disable dat
*/
audiobus_update_bits(EE_AUDIO_TOHDMITX_CTRL0,
0x1 << 28, 0x1 << 28);
audiobus_update_bits(EE_AUDIO_TOHDMITX_CTRL0,
0x1 << 29, 0x1 << 29);
} else {
audiobus_update_bits(EE_AUDIO_TOHDMITX_CTRL0,
0x1 << 31, 0x1 << 31);
}
}
void aml_tdm_mute_playback(struct aml_audio_controller *actrl,
int tdm_index, bool mute, int lane_cnt)
{
unsigned int offset, reg;
unsigned int mute_mask = 0xffffffff;
unsigned int mute_val = 0;
int i = 0;
if (mute)
mute_val = 0xffffffff;
if (tdm_index == 3) {
reg = EE_AUDIO_TDMOUT_D_MUTE0;
} else if (tdm_index < 3) {
offset = EE_AUDIO_TDMOUT_B_MUTE0
- EE_AUDIO_TDMOUT_A_MUTE0;
reg = EE_AUDIO_TDMOUT_A_MUTE0 + offset * tdm_index;
} else {
reg = EE_AUDIO_TDMOUT_A_MUTE0;
}
for (i = 0; i < LANE_MAX1; i++)
aml_audiobus_update_bits(actrl, reg + i, mute_mask, mute_val);
if (lane_cnt > LANE_MAX1) {
if (tdm_index == 3) {
reg = EE_AUDIO_TDMOUT_D_MUTE4;
} else if (tdm_index < 3) {
offset = EE_AUDIO_TDMOUT_B_MUTE4
- EE_AUDIO_TDMOUT_A_MUTE4;
reg = EE_AUDIO_TDMOUT_A_MUTE4 + offset * tdm_index;
} else {
reg = EE_AUDIO_TDMOUT_A_MUTE4;
}
for (i = 0; i < LANE_MAX1; i++)
aml_audiobus_update_bits(actrl, reg + i,
mute_mask, mute_val);
}
}
void aml_tdm_mute_capture(struct aml_audio_controller *actrl,
int tdm_index, bool mute, int lane_cnt)
{
unsigned int offset, reg;
unsigned int mute_mask = 0xffffffff;
unsigned int mute_val = 0;
int i = 0;
if (mute)
mute_val = 0xffffffff;
if (tdm_index == 3) {
reg = EE_AUDIO_TDMIN_D_MUTE0;
} else if (tdm_index < 3) {
offset = EE_AUDIO_TDMIN_B_MUTE0
- EE_AUDIO_TDMIN_A_MUTE0;
reg = EE_AUDIO_TDMIN_A_MUTE0 + offset * tdm_index;
} else {
reg = EE_AUDIO_TDMIN_A_MUTE0;
}
for (i = 0; i < LANE_MAX1; i++)
aml_audiobus_update_bits(actrl, reg + i,
mute_mask, mute_val);
if (lane_cnt > LANE_MAX1) {
if (tdm_index == 3) {
reg = EE_AUDIO_TDMIN_D_MUTE4;
} else if (tdm_index < 3) {
offset = EE_AUDIO_TDMIN_B_MUTE4
- EE_AUDIO_TDMIN_A_MUTE4;
reg = EE_AUDIO_TDMIN_A_MUTE4 + offset * tdm_index;
} else {
reg = EE_AUDIO_TDMIN_A_MUTE4;
}
for (i = 0; i < LANE_MAX1; i++)
aml_audiobus_update_bits(actrl, reg + i,
mute_mask, mute_val);
}
}
void aml_tdm_out_reset(unsigned int tdm_id, int offset)
{
unsigned int reg = 0, val = 0;
if (offset && offset != 1) {
pr_err("%s(), invalid offset = %d\n", __func__, offset);
return;
}
if (tdm_id == 0) {
reg = EE_AUDIO_SW_RESET0(offset);
val = REG_BIT_RESET_TDMOUTA;
} else if (tdm_id == 1) {
reg = EE_AUDIO_SW_RESET0(offset);
val = REG_BIT_RESET_TDMOUTB;
} else if (tdm_id == 2) {
reg = EE_AUDIO_SW_RESET0(offset);
val = REG_BIT_RESET_TDMOUTC;
} else if (tdm_id == 3) {
reg = EE_AUDIO_SW_RESET1(offset);
val = REG_BIT_RESET_TDMOUTD;
} else {
pr_err("invalid tdmout id %d\n", tdm_id);
return;
}
audiobus_update_bits(reg, val, val);
audiobus_update_bits(reg, val, 0);
}
void aml_tdmout_auto_gain_enable(unsigned int tdm_id)
{
unsigned int reg, offset;
if (tdm_id == 3) {
reg = EE_AUDIO_TDMOUT_D_GAIN_EN;
} else if (tdm_id < 3) {
offset = EE_AUDIO_TDMOUT_B_GAIN_EN - EE_AUDIO_TDMOUT_A_GAIN_EN;
reg = EE_AUDIO_TDMOUT_A_GAIN_EN + offset * tdm_id;
} else {
reg = EE_AUDIO_TDMOUT_A_GAIN_EN;
}
/* 0 - 8 channel */
audiobus_update_bits(reg, 0xFF << 0, 0xFF << 0);
if (tdm_id == 3) {
reg = EE_AUDIO_TDMOUT_D_GAIN_CTRL;
} else if (tdm_id < 3) {
offset = EE_AUDIO_TDMOUT_B_GAIN_CTRL - EE_AUDIO_TDMOUT_A_GAIN_CTRL;
reg = EE_AUDIO_TDMOUT_A_GAIN_CTRL + offset * tdm_id;
} else {
reg = EE_AUDIO_TDMOUT_A_GAIN_CTRL;
}
/*
* bit 31: step by step change gain
* bit 16 - 23: gain_step
* bit 0 - 15: the period of each change, unit is FS
*/
audiobus_update_bits(reg,
0x1 << 31 | 0xFF << 16 | 0xFFFF << 0,
0x1 << 31 | 0x0C << 16 | 0x03C0 << 0);
}
void aml_tdmout_set_gain(int tdmout_id, int value)
{
unsigned int reg, offset;
int i;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_GAIN0;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_GAIN0 - EE_AUDIO_TDMOUT_A_GAIN0;
reg = EE_AUDIO_TDMOUT_A_GAIN0 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_GAIN0;
}
/* channel 0 - channel 8 */
for (i = 0; i < 2; i++)
audiobus_write(reg + i,
value << 24
| value << 16
| value << 8
| value << 0);
}
int aml_tdmout_get_gain(int tdmout_id)
{
unsigned int reg, offset;
int value;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_GAIN0;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_GAIN0 - EE_AUDIO_TDMOUT_A_GAIN0;
reg = EE_AUDIO_TDMOUT_A_GAIN0 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_GAIN0;
}
value = audiobus_read(reg);
return value & 0xFF;
}
void aml_tdmout_set_mute(int tdmout_id, int mute)
{
unsigned int reg, offset, value;
int i;
if (mute)
value = 0xFFFFFFFF;
else
value = 0x0;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_MUTE0;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_MUTE0 - EE_AUDIO_TDMOUT_A_MUTE0;
reg = EE_AUDIO_TDMOUT_A_MUTE0 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_MUTE0;
}
/* lane0 - lane3 */
for (i = 0; i < 4; i++)
audiobus_write(reg + i, value);
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_MUTE4;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_MUTE4 - EE_AUDIO_TDMOUT_A_MUTE4;
reg = EE_AUDIO_TDMOUT_A_MUTE4 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_MUTE4;
}
/* lane4 - lane7 */
for (i = 0; i < 4; i++)
audiobus_write(reg + i, value);
}
int aml_tdmout_get_mute(int tdmout_id)
{
unsigned int reg, offset;
int value;
if (tdmout_id == 3) {
reg = EE_AUDIO_TDMOUT_D_MUTE0;
} else if (tdmout_id < 3) {
offset = EE_AUDIO_TDMOUT_B_MUTE0 - EE_AUDIO_TDMOUT_A_MUTE0;
reg = EE_AUDIO_TDMOUT_A_MUTE0 + offset * tdmout_id;
} else {
reg = EE_AUDIO_TDMOUT_A_MUTE0;
}
value = audiobus_read(reg);
return value & 0x1;
}
int aml_tdmin_get_status(int tdm_id)
{
unsigned int reg, offset;
if (tdm_id == 3) {
reg = EE_AUDIO_TDMIN_D_STAT;
} else if (tdm_id < 3) {
offset = EE_AUDIO_TDMIN_B_STAT - EE_AUDIO_TDMIN_A_STAT;
reg = EE_AUDIO_TDMIN_A_STAT + offset * tdm_id;
} else {
reg = EE_AUDIO_TDMIN_A_STAT;
}
return audiobus_read(reg);
}
void aml_tdmin_set_slot_num(struct aml_audio_controller *actrl, int index, int slot_num)
{
unsigned int reg, offset;
if (index == 3) {
reg = EE_AUDIO_TDMIN_D_CTRL;
} else if (index < 3) {
offset = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL;
reg = EE_AUDIO_TDMIN_A_CTRL + offset * index;
} else {
reg = EE_AUDIO_TDMIN_A_CTRL;
}
aml_audiobus_update_bits(actrl, reg, 0x1f << 8, (slot_num - 1) << 8);
}