320 lines
8.2 KiB
C
320 lines
8.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
|
|
* Author: Jason Zhang <jason.zhang@rock-chips.com>
|
|
*/
|
|
|
|
#include "it6621.h"
|
|
#include "it6621-clk.h"
|
|
#include "it6621-earc.h"
|
|
#include "it6621-reg-bank0.h"
|
|
#include "it6621-reg-bank1.h"
|
|
|
|
static int it6621_get_100ms_cnt(struct it6621_priv *priv, u32 *count)
|
|
{
|
|
unsigned int val;
|
|
|
|
regmap_write(priv->regmap, IT6621_REG_GEN0, IT6621_100MS_CNT_EN);
|
|
msleep(99);
|
|
regmap_write(priv->regmap, IT6621_REG_GEN0, IT6621_100MS_CNT_DIS);
|
|
regmap_read(priv->regmap, IT6621_REG_100MS_CNT_7_0, &val);
|
|
*count = (u32)val;
|
|
regmap_read(priv->regmap, IT6621_REG_100MS_CNT_15_8, &val);
|
|
*count += ((u32)(val) << 8);
|
|
regmap_read(priv->regmap, IT6621_REG_100MS_CNT_23_16, &val);
|
|
*count += ((u32)(val) << 16);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int it6621_get_lcf(struct it6621_priv *priv, unsigned int *lcf)
|
|
{
|
|
unsigned int timer_1us_flt;
|
|
unsigned int lcf_high;
|
|
unsigned int lcf_low;
|
|
unsigned int tmp;
|
|
|
|
if (priv->fixed_lcf) {
|
|
*lcf = priv->fixed_lcf;
|
|
dev_info(priv->dev, "lcf: %d\n", *lcf);
|
|
} else {
|
|
regmap_update_bits(priv->regmap, 0xa9, 0x0f, 0x00);
|
|
it6621_get_100ms_cnt(priv, &lcf_low);
|
|
|
|
regmap_update_bits(priv->regmap, 0xa9, 0x0f, 0x0f);
|
|
it6621_get_100ms_cnt(priv, &lcf_high);
|
|
|
|
if (!(lcf_high - lcf_low)) {
|
|
dev_err(priv->dev, "Invalid LCF");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Get tenths */
|
|
tmp = (1000000 - lcf_low) * 16 * 10 / (lcf_high - lcf_low);
|
|
timer_1us_flt = tmp % 10;
|
|
tmp = tmp / 10;
|
|
if (timer_1us_flt >= 5)
|
|
*lcf = tmp + 1;
|
|
else
|
|
*lcf = tmp;
|
|
|
|
dev_info(priv->dev, "lcf low: %d, lcf high: %d, lcf: %d\n",
|
|
lcf_low, lcf_high, *lcf);
|
|
}
|
|
|
|
if (*lcf > 15)
|
|
*lcf = 15;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int it6621_get_refclk(struct it6621_priv *priv, unsigned int *refclk)
|
|
{
|
|
if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK)
|
|
*refclk = priv->rclk / 2;
|
|
else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_2)
|
|
*refclk = priv->rclk;
|
|
else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_4)
|
|
*refclk = priv->rclk * 2;
|
|
else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_8)
|
|
*refclk = priv->rclk * 4;
|
|
else
|
|
*refclk = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int it6621_calc_rclk(struct it6621_priv *priv)
|
|
{
|
|
unsigned int timer_1us;
|
|
unsigned int timer_1us_int;
|
|
unsigned int timer_1us_flt;
|
|
unsigned int lcf;
|
|
unsigned int aclk_bnd_num;
|
|
unsigned int aclk_valid_num;
|
|
unsigned int sum;
|
|
int ret;
|
|
|
|
ret = it6621_get_lcf(priv, &lcf);
|
|
if (ret)
|
|
return ret;
|
|
|
|
regmap_update_bits(priv->regmap, 0xa9, 0x0f, lcf);
|
|
|
|
it6621_get_100ms_cnt(priv, &sum);
|
|
priv->rclk = sum / 100;
|
|
dev_dbg(priv->dev, "RCLK: %uMHz\n", priv->rclk / 1000);
|
|
|
|
/* Update 1us timer */
|
|
timer_1us = sum / 100000;
|
|
timer_1us_int = timer_1us;
|
|
timer_1us_flt = sum % 100000;
|
|
timer_1us_flt <<= 8;
|
|
timer_1us_flt /= 100000;
|
|
regmap_write(priv->regmap, IT6621_REG_GEN4, timer_1us_int);
|
|
regmap_write(priv->regmap, IT6621_REG_1US_TIME_FLT, timer_1us_flt);
|
|
|
|
if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK)
|
|
sum /= 2;
|
|
else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_4)
|
|
sum *= 2;
|
|
else if (priv->rclk_sel == IT6621_RCLK_FREQ_REFCLK_DIV_8)
|
|
sum *= 4;
|
|
|
|
aclk_bnd_num = sum * 128 / 358400;
|
|
aclk_valid_num = sum * 128 / 200000;
|
|
regmap_write(priv->regmap, IT6621_REG_CLK_DET0, aclk_bnd_num & 0xff);
|
|
regmap_write(priv->regmap, IT6621_REG_CLK_DET1,
|
|
(aclk_bnd_num & 0x100) >> 8);
|
|
regmap_write(priv->regmap, IT6621_REG_CLK_DET2,
|
|
aclk_valid_num & 0xff);
|
|
regmap_write(priv->regmap, IT6621_REG_CLK_DET3,
|
|
(aclk_valid_num & 0x300) >> 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* it6621_force_pdiv() - If auto mode error , FW force to correct PLL's setting
|
|
* @priv: The priv device
|
|
*
|
|
* Returns zero if succeed.
|
|
*/
|
|
int it6621_force_pdiv(struct it6621_priv *priv)
|
|
{
|
|
unsigned int tbclk_sel;
|
|
unsigned int xp_pdiv;
|
|
unsigned int xp_gain;
|
|
unsigned int val;
|
|
unsigned int expect;
|
|
|
|
regmap_read(priv->regmap, IT6621_REG_CLK_CTRL1, &val);
|
|
tbclk_sel = val & IT6621_TBCLK_SEL;
|
|
|
|
regmap_read(priv->regmap, IT6621_REG_TX_AFE1, &val);
|
|
xp_pdiv = val & IT6621_TX_XP_PDIV;
|
|
xp_gain = (val & IT6621_TX_XP_GAIN) >> 2;
|
|
|
|
dev_dbg(priv->dev, "ACLK: %d\n", priv->aclk);
|
|
|
|
expect = 0xff;
|
|
switch (tbclk_sel) {
|
|
case IT6621_TBCLK_AICLK_MUL_32:
|
|
if (xp_pdiv != 0)
|
|
expect = 0;
|
|
break;
|
|
case IT6621_TBCLK_AICLK_MUL_16:
|
|
if ((priv->aclk < 3584) && (xp_pdiv != 0))
|
|
expect = 0;
|
|
else if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
|
|
(xp_pdiv != 1))
|
|
expect = 1;
|
|
break;
|
|
case IT6621_TBCLK_AICLK_MUL_8:
|
|
if ((priv->aclk < 3584) && (xp_pdiv != 0))
|
|
expect = 0;
|
|
else if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
|
|
(xp_pdiv != 1))
|
|
expect = 1;
|
|
else if ((priv->aclk > 7168) && (priv->aclk < 14336) &&
|
|
(xp_pdiv != 3))
|
|
expect = 3;
|
|
break;
|
|
case IT6621_TBCLK_AICLK_MUL_4:
|
|
if ((priv->aclk < 7168) && xp_pdiv != 0)
|
|
expect = 0;
|
|
else if ((priv->aclk > 7168) && (priv->aclk < 14336) &&
|
|
(xp_pdiv != 1))
|
|
expect = 1;
|
|
else if ((priv->aclk > 14336) && (priv->aclk < 28672) &&
|
|
(xp_pdiv != 3))
|
|
expect = 3;
|
|
break;
|
|
case IT6621_TBCLK_AICLK_MUL_2:
|
|
if ((priv->aclk < 14336) && xp_pdiv != 0)
|
|
expect = 0;
|
|
else if ((priv->aclk > 14336) && (priv->aclk < 28672) &&
|
|
(xp_pdiv != 1))
|
|
expect = 1;
|
|
else if ((priv->aclk > 28672) && (priv->aclk < 57334) &&
|
|
(xp_pdiv != 3))
|
|
expect = 3;
|
|
break;
|
|
case IT6621_TBCLK_AICLK:
|
|
if ((priv->aclk < 28672) && (xp_pdiv != 0))
|
|
expect = 0;
|
|
else if ((priv->aclk > 28672) && (priv->aclk < 57334) &&
|
|
(xp_pdiv != 1))
|
|
expect = 1;
|
|
else if ((priv->aclk > 57334) && (priv->aclk < 114668) &&
|
|
(xp_pdiv != 3))
|
|
expect = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (expect != 0xff)
|
|
dev_warn(priv->dev, "Wrong XP PDIV, detect %d, expected %d\n",
|
|
xp_pdiv, expect);
|
|
|
|
expect = 0xff;
|
|
switch (tbclk_sel) {
|
|
case IT6621_TBCLK_AICLK:
|
|
if ((priv->aclk > 3584) && (priv->aclk < 7168) &&
|
|
(xp_gain != 0))
|
|
expect = 0;
|
|
break;
|
|
case IT6621_TBCLK_AICLK_MUL_2:
|
|
if ((priv->aclk < 3584) && (xp_gain != 0))
|
|
expect = 0;
|
|
break;
|
|
default:
|
|
if (xp_gain != 1)
|
|
expect = 1;
|
|
break;
|
|
}
|
|
|
|
if (expect != 0xff)
|
|
dev_warn(priv->dev, "Wrong XP GAIN, detect %d, expected %d\n",
|
|
xp_gain, expect);
|
|
|
|
regmap_read(priv->regmap, IT6621_REG_TX_AFE1, &val);
|
|
xp_pdiv = val & IT6621_TX_XP_PDIV;
|
|
xp_gain = (val & IT6621_TX_XP_GAIN) >> 2;
|
|
dev_dbg(priv->dev, "TBCLK SEL: 0x%02X, XP PDIV: %d, XP GAIN: %d\n",
|
|
tbclk_sel, xp_pdiv, xp_gain);
|
|
|
|
usleep_range(10000, 11000);
|
|
regmap_update_bits(priv->regmap, IT6621_REG_DMAC_CTRL1,
|
|
IT6621_TX_MANUAL_RESET_EN_SEL,
|
|
IT6621_TX_MANUAL_RESET_EN);
|
|
regmap_update_bits(priv->regmap, IT6621_REG_DMAC_CTRL1,
|
|
IT6621_TX_MANUAL_RESET_EN_SEL,
|
|
IT6621_TX_MANUAL_RESET_DIS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void it6621_get_aclk(struct it6621_priv *priv)
|
|
{
|
|
unsigned int aclk_avg;
|
|
unsigned int aclk_pred2;
|
|
unsigned int aclk_pred4;
|
|
unsigned int aclk_valid;
|
|
unsigned int aclk_stb;
|
|
unsigned int refclk;
|
|
unsigned int tmp;
|
|
|
|
it6621_get_refclk(priv, &refclk);
|
|
|
|
regmap_read(priv->regmap, IT6621_REG_CLK_DET8, &aclk_avg);
|
|
regmap_read(priv->regmap, IT6621_REG_CLK_DET9, &tmp);
|
|
aclk_avg += ((tmp & IT6621_DET_ACLK_AVG_HIGH) << 8);
|
|
aclk_pred2 = (tmp & IT6621_DET_ACLK_PRE_DIV_2) >> 4;
|
|
aclk_pred4 = (tmp & IT6621_DET_ACLK_PRE_DIV_4) >> 5;
|
|
aclk_valid = (tmp & IT6621_DET_ACLK_FREQ_VALID) >> 6;
|
|
aclk_stb = (tmp & IT6621_DET_ACLK_FREQ_STB) >> 7;
|
|
|
|
priv->aclk = refclk * 128 / aclk_avg;
|
|
|
|
if (aclk_pred4)
|
|
priv->aclk *= 4;
|
|
else if (aclk_pred2)
|
|
priv->aclk *= 2;
|
|
|
|
dev_dbg(priv->dev, "ACLK: %uMHz, valid: %d, stable: %d\n",
|
|
priv->aclk / 1000, aclk_valid, aclk_stb);
|
|
}
|
|
|
|
void it6621_get_bclk(struct it6621_priv *priv)
|
|
{
|
|
unsigned int bclk_avg;
|
|
unsigned int bclk_pred2;
|
|
unsigned int bclk_pred4;
|
|
unsigned int bclk_valid;
|
|
unsigned int bclk_stb;
|
|
unsigned int refclk;
|
|
unsigned int tmp;
|
|
|
|
it6621_get_refclk(priv, &refclk);
|
|
|
|
regmap_read(priv->regmap, IT6621_REG_CLK_DET10, &bclk_avg);
|
|
regmap_read(priv->regmap, IT6621_REG_CLK_DET11, &tmp);
|
|
bclk_avg += ((tmp & IT6621_DET_BCLK_AVG_HIGH) << 8);
|
|
bclk_pred2 = (tmp & IT6621_DET_BCLK_PRE_DIV_2) >> 4;
|
|
bclk_pred4 = (tmp & IT6621_DET_BCLK_PRE_DIV_4) >> 5;
|
|
bclk_valid = (tmp & IT6621_DET_BCLK_FREQ_VALID) >> 6;
|
|
bclk_stb = (tmp & IT6621_DET_BCLK_FREQ_STB) >> 7;
|
|
|
|
priv->bclk = refclk * 128 / bclk_avg;
|
|
|
|
if (bclk_pred4)
|
|
priv->bclk *= 4;
|
|
else if (bclk_pred2)
|
|
priv->bclk *= 2;
|
|
|
|
dev_dbg(priv->dev, "BCLK: %uMHz, valid: %d, stable: %d\n",
|
|
priv->bclk / 1000, bclk_valid, bclk_stb);
|
|
}
|