blob: c792ce0b5b914046df029c7b14f0c5351dcfc13a [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/amlogic/iomap.h>
#include "frhdmirx_hw.h"
#include "regs.h"
#include "iomap.h"
#include "extn.h"
#include "hdmirx_arc_iomap.h"
#include "earc.h"
void frhdmirx_afifo_reset(void)
{
unsigned int enable = audiobus_read(EE_AUDIO_FRHDMIRX_CTRL0) >> 31;
if (enable) {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0,
0x1 << 29, 0x0 << 29);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0,
0x1 << 29, 0x1 << 29);
}
}
void frhdmirx_enable(bool enable, int version)
{
if (enable) {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x1 << 29, 0x1 << 29);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x1 << 28, 0x1 << 28);
} else {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x3 << 28, 0x0 << 28);
}
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x1 << 31, enable << 31);
/* from tm2 revb, need enable pao separately */
if (version != T7_FRHDMIRX)
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x1 << 19, enable << 19);
}
/* source select
* 0: select spdif lane;
* 1: select PAO mode;
*/
void frhdmirx_src_select(int src)
{
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0,
0x1 << 23,
(bool)src << 23);
}
static void frhdmirx_enable_irq_bits(int channels, int src, int version)
{
unsigned int int_bits = 0;
/* interrupt bits */
if (src) { /* PAO mode */
int_bits = (0x1 << INT_PAO_PAPB_MASK | /* find papb */
0x1 << INT_PAO_PCPD_MASK /* find pcpd changed */
);
} else { /* SPDIF Lane */
int lane_irq_bits = (0x1 << 7 | /* lane: find papb */
0x1 << 6 | /* lane: find valid changed */
0x1 << 5 | /* lane: find nonpcm to pcm */
0x1 << 4 | /* lane: find pcpd changed */
0x1 << 3 | /* lane: find ch status changed */
0x1 << 1 /* lane: find parity error */
);
int lane, i;
lane = (channels % 2) ? (channels / 2 + 1) : (channels / 2);
for (i = 0; i < lane; i++)
int_bits |= (lane_irq_bits << 8 * i);
}
if (version != T7_FRHDMIRX) {
int_bits |= audiobus_read(EE_AUDIO_FRHDMIRX_CTRL2);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL2, int_bits);
} else {
/* only 1 lane on t7 version */
int_bits = (int_bits << 20) & (0xff << 20);
int_bits |= audiobus_read(EE_AUDIO_FRHDMIRX_CTRL1);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL1, int_bits);
}
}
void frhdmirx_clr_all_irq_bits(int version)
{
if (version != T7_FRHDMIRX) {
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL3, 0xffffffff);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL3, 0x0);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL4, 0xffffffff);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL4, 0x0);
} else {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xffff << 16, 0xffff << 16);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xffff << 16, 0 << 16);
}
}
void frhdmirx_ctrl(int channels, int src, int version)
{
int lane, lane_mask = 0, i;
lane = (channels % 2) ? (channels / 2 + 1) : (channels / 2);
for (i = 0; i < lane; i++)
lane_mask |= (1 << i);
/* PAO mode */
if (src && version != T7_FRHDMIRX) {
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL0,
lane_mask << 24 | /* chnum_sel */
0x1 << 22 | /* capture input by fall edge*/
0x1 << 8 | /* start detect PAPB */
0x1 << 7 | /* add channel num */
0x4 << 4 /* chan status sel: pao pc/pd value */
);
} else {
if (version == T7_FRHDMIRX) {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0,
0x1 << 24 |
0x1 << 25 |
0x1 << 26 |
0x7 << 8,
0x1 << 24 | /* clk_inv */
0x1 << 25 | /* start detect PAPB */
0x1 << 26 | /* add channel num*/
0x6 << 8 /* channel status*/
);
} else {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0,
0x1 << 30 | 0xf << 24 | 0x1 << 22 |
0x3 << 11 | 0x1 << 8 | 0x1 << 7 | 0x7 << 0,
0x1 << 30 | /* chnum_sel */
lane_mask << 24 | /* chnum_sel */
0x1 << 22 | /* clk_inv */
0x0 << 11 | /* req_sel, Sync 4 spdifin by which */
0x1 << 8 | /* start detect PAPB */
0x1 << 7 | /* add channel num*/
0x6 << 0 /* channel status*/
);
}
}
/* nonpcm2pcm_th */
if (version == T7_FRHDMIRX)
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0xff << 12, 0xff << 12);
else
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL1, 0xff << 20);
/* enable irq bits */
frhdmirx_enable_irq_bits(channels, src, version);
}
void frhdmirx_clr_PAO_irq_bits(void)
{
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL4,
0x1 << INT_PAO_PAPB_MASK | 0x1 << INT_PAO_PCPD_MASK,
0x1 << INT_PAO_PAPB_MASK | 0x1 << INT_PAO_PCPD_MASK);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL4,
0x1 << INT_PAO_PAPB_MASK | 0x1 << INT_PAO_PCPD_MASK,
0x0 << INT_PAO_PAPB_MASK | 0x0 << INT_PAO_PCPD_MASK);
}
void frhdmirx_clr_SPDIF_irq_bits(void)
{
unsigned int value = audiobus_read(EE_AUDIO_FRHDMIRX_STAT0);
unsigned int clr_mask = audiobus_read(EE_AUDIO_FRHDMIRX_CTRL4);
unsigned int reg = 0;
int i;
reg = clr_mask | value;
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL4, reg);
reg = clr_mask & (~value);
audiobus_write(EE_AUDIO_FRHDMIRX_CTRL4, reg);
/*compressed audio only transfer through lane0*/
for (i = 0; i < 1; i++) {
/*nonpcm2pcm irq, clear papb/pcpd/nonpcm*/
if (value & (0x20 << 8 * i)) {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL3,
0xf << 8 * i, 0xf << 8 * i);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL3,
0xf << 8 * i, 0x0 << 8 * i);
pr_info("raw to pcm change: irq status:%x, lane: %d\n",
value, i);
}
}
}
void frhdmirx_clr_SPDIF_irq_bits_for_t7_version(void)
{
unsigned int value = audiobus_read(EE_AUDIO_FRHDMIRX_STAT0) & 0xff;
unsigned int clr_mask = (audiobus_read(EE_AUDIO_FRHDMIRX_CTRL6) >> 16) & 0xff;
unsigned int reg = 0;
reg = clr_mask | value;
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xff << 16, reg << 16);
reg = clr_mask & (~value);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xff << 16, reg << 16);
if (value & 0x20) {
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xf << 24, 0xf << 24);
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL6, 0xf << 24, 0 << 24);
pr_info("t7 raw to pcm change: irq status:%x\n", value);
}
}
/*
* reg_ status_sel[6:4];
* 0: spdif lane0;
* 1: spdif lane1;
* 2: spdif lane2;
* 3: spdif lane3;
* 4: pao pc/pd value;
* 5: valid bits;
*/
static void frhdmirx_set_reg_status_sel(uint32_t sel)
{
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0x7 << 4, sel << 4);
}
/*
* reg_status_sel[3]: 0: channel A; 1: channel B;
* reg_status_sel[2:0]:
* 0: ch_status[31:0];
* 1: ch_status[63:32];
* 2: ch_status[95:64];
* 3: ch_status[127:96];
* 4: ch_status[159:128];
* 5: ch_status[191:160];
* 6: pc[15:0],pd[15:0];
*/
static void frhdmirx_spdif_channel_status_sel(u32 sel, int version)
{
/* alway select chanel A */
if (version == T7_FRHDMIRX)
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0xf << 8, sel << 8);
else
audiobus_update_bits(EE_AUDIO_FRHDMIRX_CTRL0, 0xf, sel);
}
static DEFINE_SPINLOCK(frhdmirx_lock);
unsigned int frhdmirx_get_ch_status(int num, int version)
{
unsigned int val;
unsigned long flags = 0;
spin_lock_irqsave(&frhdmirx_lock, flags);
/* default spdif lane 0, channal A */
if (version != T7_FRHDMIRX)
frhdmirx_set_reg_status_sel(0);
frhdmirx_spdif_channel_status_sel(num, version);
val = audiobus_read(EE_AUDIO_FRHDMIRX_STAT1);
spin_unlock_irqrestore(&frhdmirx_lock, flags);
return val;
}
unsigned int frhdmirx_get_chan_status_pc(enum hdmirx_mode mode, int version)
{
unsigned int val;
unsigned long flags = 0;
spin_lock_irqsave(&frhdmirx_lock, flags);
if (mode == HDMIRX_MODE_PAO && version != T7_FRHDMIRX) {
frhdmirx_set_reg_status_sel(4);
} else if (mode == HDMIRX_MODE_SPDIFIN) {
if (version != T7_FRHDMIRX)
frhdmirx_set_reg_status_sel(0);
frhdmirx_spdif_channel_status_sel(6, version);
}
val = audiobus_read(EE_AUDIO_FRHDMIRX_STAT1);
spin_unlock_irqrestore(&frhdmirx_lock, flags);
return (val >> 16) & 0xff;
}
/* this is used for TL1, arc source select and enable */
void arc_source_enable(int src, bool enable)
{
/* open ARC bandgap, bit [1] = 0 */
if (enable)
aml_hiubus_update_bits(HHI_HDMIRX_PHY_MISC2,
0x1 << 1, 0 << 1);
/* bits[1:0], 0x2: common; 0x1: single; 0x0: disabled */
aml_hiubus_update_bits(HHI_HDMIRX_ARC_CNTL,
0x1f << 0,
src << 2 | (enable ? 0x1 : 0) << 0);
}
/* this is used from TM2, arc/earc source select */
void arc_earc_source_select(int src)
{
int version = get_arc_version();
if (src == SPDIFA_TO_HDMIRX || src == SPDIFB_TO_HDMIRX) {
if (version == TM2_ARC) {
/* spdif_a = 1; spdif_b = 2*/
aml_write_hiubus(HHI_HDMIRX_ARC_CNTL, 0xfffffff8 | src);
/* analog registers: single mode, about 520mv*/
aml_write_hiubus(HHI_HDMIRX_EARCTX_CNTL0, 0x14710490);
aml_write_hiubus(HHI_HDMIRX_EARCTX_CNTL1, 0x40011508);
} else {
hdmirx_arc_write_reg(HDMIRX_ARC_CNTL, 0xfffffff8 | src);
hdmirx_arc_write_reg(HDMIRX_EARCTX_CNTL0, 0x94830490);
hdmirx_arc_write_reg(HDMIRX_EARCTX_CNTL1, 0x40011508);
}
} else {
/* earctx_spdif*/
if (version == TM2_ARC)
aml_write_hiubus(HHI_HDMIRX_ARC_CNTL, 0x0);
else
hdmirx_arc_write_reg(HDMIRX_ARC_CNTL, 0x0);
}
}
/* this is used from TM2, arc source enable */
void arc_enable(bool enable, int version)
{
/*
* 1. can't disable earc as the heartbeat lost
* 2. can't disable earc during reset hpd by earc driver
*/
if (aml_get_earctx_connected_device_type() == ATNDTYP_EARC ||
aml_get_earctx_reset_hpd())
return;
pr_info("%s %d\n", __func__, enable);
if (is_earc_spdif()) {
aml_earctx_enable_d2a(enable);
} else {
if (version == TM2_ARC) {
/* open ARC bandgap, bit [1] = 0 */
if (enable)
aml_hiubus_update_bits(HHI_HDMIRX_PHY_MISC2,
0x1 << 1, 0 << 1);
aml_hiubus_update_bits(HHI_HDMIRX_EARCTX_CNTL0,
0x1 << 31, (enable ? 0x1 : 0) << 31);
} else if (version == T7_ARC) {
/* open ARC bandgap, bit [1] = 0 */
if (enable)
hdmirx_arc_update_reg(HDMIRX_PHY_MISC2,
0x1 << 1, 0 << 1);
hdmirx_arc_update_reg(HDMIRX_EARCTX_CNTL0,
0x1 << 31, (enable ? 0x1 : 0) << 31);
}
}
}