blob: dcca87844a310a03e03ee6dd3f1c5ccbeaf1b667 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#define DEBUG
#include "linux/kernel.h"
#include "loopback_hw.h"
#include "regs.h"
#include "iomap.h"
static unsigned int get_tdmin_id_from_lb_src(enum datalb_src lb_src)
{
return lb_src % TDMINLB_PAD_TDMINA;
}
void tdminlb_set_clk(enum datalb_src lb_src,
int sclk_div, int ratio, bool enable)
{
unsigned int bclk_sel, fsclk_sel;
unsigned int tdmin_src;
/* config for external codec */
if (lb_src >= TDMINLB_PAD_TDMINA) {
unsigned int id = get_tdmin_id_from_lb_src(lb_src);
unsigned int offset, reg;
unsigned int fsclk_hi;
fsclk_hi = ratio / 2;
bclk_sel = id;
fsclk_sel = id;
/*sclk, lrclk*/
offset = EE_AUDIO_MST_B_SCLK_CTRL0 - EE_AUDIO_MST_A_SCLK_CTRL0;
reg = EE_AUDIO_MST_A_SCLK_CTRL0 + offset * id;
audiobus_update_bits(reg,
0x3 << 30 | 0x3ff << 20 |
0x3ff << 10 | 0x3ff,
(enable ? 0x3 : 0x0) << 30 |
sclk_div << 20 | fsclk_hi << 10 |
ratio);
tdmin_src = id;
} else {
tdmin_src = lb_src;
}
audiobus_update_bits(EE_AUDIO_CLK_TDMIN_LB_CTRL,
0x3 << 30 | 1 << 29 | 0xf << 24 | 0xf << 20,
(enable ? 0x3 : 0x0) << 30 |
1 << 29 | tdmin_src << 24 | tdmin_src << 20
);
}
void tdminlb_enable(int tdm_index, int is_enable)
{
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL,
0x1 << 31, is_enable << 31);
}
void tdminlb_set_format(int i2s_fmt)
{
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL,
0x1 << 30,
!!i2s_fmt << 30 /* 0:tdm mode; 1: i2s mode; */
);
}
void tdminlb_set_ctrl(enum datalb_src src)
{
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL,
0xf << 20 | 0x7 << 16 | 0x1f << 0,
src << 20 | /* in src */
3 << 16 | /* skew */
31 << 0 /* bit width */
);
}
void tdminlb_fifo_enable(int is_enable)
{
if (is_enable) {
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 1 << 29, 1 << 29);
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 1 << 28, 1 << 28);
} else {
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 3 << 28, 0);
}
}
static void tdminlb_set_lane_mask(int lane, int mask)
{
audiobus_write(EE_AUDIO_TDMIN_LB_MASK0 + lane, mask);
}
void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask, unsigned int mask)
{
unsigned int i;
pr_debug
("%s() lane swap:0x%x lane mask:0x%x, chmask:%#x\n",
__func__, swap, lane_mask, mask);
/* channel swap */
audiobus_write(EE_AUDIO_TDMIN_LB_SWAP0, swap);
/* lane mask */
for (i = 0; i < 4; i++)
if ((1 << i) & lane_mask)
tdminlb_set_lane_mask(i, mask);
}
void tdminlb_set_src(int src)
{
audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 0xf << 20, src);
}
void lb_set_datain_src(int id, int src)
{
int offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
int reg = EE_AUDIO_LB_A_CTRL0 + offset * id;
audiobus_update_bits(reg, 0x7 << 0, src);
}
void lb_set_mode(int id, enum lb_out_rate rate)
{
int offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
int reg = EE_AUDIO_LB_A_CTRL0 + offset * id;
audiobus_update_bits(reg, 0x1 << 30, rate << 30);
}
void loopback_src_set(int id, struct mux_conf *conf, int version)
{
int offset = 0;
int reg = 0;
if (version == 0) {
offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
reg = EE_AUDIO_LB_B_CTRL0 + offset * id;
} else {
offset = EE_AUDIO_LB_B_CTRL2 - EE_AUDIO_LB_A_CTRL2;
reg = EE_AUDIO_LB_A_CTRL2 + offset * id;
}
audiobus_update_bits(reg, conf->mask << conf->shift, conf->val << conf->shift);
}
void lb_set_datain_cfg(int id, struct data_cfg *datain_cfg)
{
int offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
int reg = EE_AUDIO_LB_A_CTRL0 + offset * id;
if (!datain_cfg)
return;
if (datain_cfg->ch_ctrl_switch) {
audiobus_update_bits(reg,
1 << 29 | 0x7 << 13 | 0x1f << 8 |
0x1f << 3 | 0x7 << 0,
datain_cfg->ext_signed << 29 |
datain_cfg->type << 13 |
datain_cfg->m << 8 |
datain_cfg->n << 3 |
datain_cfg->src << 0
);
/* channel and mask */
offset = EE_AUDIO_LB_B_CTRL2 - EE_AUDIO_LB_A_CTRL2;
reg = EE_AUDIO_LB_A_CTRL2 + offset * id;
audiobus_write(reg,
(datain_cfg->chnum - 1) << 16 |
datain_cfg->chmask << 0
);
} else {
audiobus_update_bits(reg,
1 << 29 | 0x7 << 24 | 0xff << 16 |
0x7 << 13 | 0x1f << 8 |
0x1f << 3 | 0x7 << 0,
datain_cfg->ext_signed << 29 |
(datain_cfg->chnum - 1) << 24 |
datain_cfg->chmask << 16 |
datain_cfg->type << 13 |
datain_cfg->m << 8 |
datain_cfg->n << 3 |
datain_cfg->src << 0
);
}
}
void lb_set_datalb_cfg(int id, struct data_cfg *datalb_cfg, bool multi_bits_lbsrcs)
{
int offset = EE_AUDIO_LB_B_CTRL1 - EE_AUDIO_LB_A_CTRL1;
int reg = EE_AUDIO_LB_A_CTRL1 + offset * id;
if (!datalb_cfg)
return;
if (datalb_cfg->ch_ctrl_switch) {
audiobus_update_bits(reg,
0x1 << 29 | 0x7 << 13 |
0x1f << 8 | 0x1f << 3,
datalb_cfg->ext_signed << 29 |
datalb_cfg->type << 13 |
datalb_cfg->m << 8 |
datalb_cfg->n << 3);
if (!multi_bits_lbsrcs)
audiobus_update_bits(reg,
0x3 << 30 | 0x1 << 0,
datalb_cfg->resample_enable << 30 | datalb_cfg->loopback_src << 0);
/* channel and mask */
offset = EE_AUDIO_LB_B_CTRL3 - EE_AUDIO_LB_A_CTRL3;
reg = EE_AUDIO_LB_A_CTRL3 + offset * id;
audiobus_write(reg,
(datalb_cfg->chnum - 1) << 16 |
datalb_cfg->chmask << 0);
if (multi_bits_lbsrcs) {
/* from t5 chip, loopback src changed if resample for loopback */
int loopback_src;
if (datalb_cfg->resample_enable)
loopback_src = RESAMPLEB;
else
loopback_src = TDMIN_LB;
audiobus_update_bits(reg, 0x1f << 20, loopback_src << 20);
}
} else {
audiobus_write(reg,
datalb_cfg->ext_signed << 29 |
(datalb_cfg->chnum - 1) << 24 |
datalb_cfg->chmask << 16 |
datalb_cfg->type << 13 |
datalb_cfg->m << 8 |
datalb_cfg->n << 3
);
}
}
void lb_enable(int id, bool enable, bool chnum_en)
{
int offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
int reg = EE_AUDIO_LB_A_CTRL0 + offset * id;
audiobus_update_bits(reg, 0x1 << 31, enable << 31);
lb_set_chnum_en(id, enable, chnum_en);
}
void lb_set_chnum_en(int id, bool en, bool chnum_en)
{
if (chnum_en) {
int offset = EE_AUDIO_LB_B_CTRL0 - EE_AUDIO_LB_A_CTRL0;
int reg = EE_AUDIO_LB_A_CTRL0 + offset * id;
audiobus_update_bits(reg, 0x1 << 27, en << 27);
}
}