mfd: Add rockchip rkx110/x120 serdes support

support display topologys as follow:
1 video source input, 1 channel, 1 lane, 1 remote, 1 video output:
+-------+          +---------+            +---------+         +--------+
|       | disp in  |         |   cable0   |         | disp out|        |
|  soc  |--------->|  RK110  +----------->|  RK120  +-------->| screen |
|       |          |         |            |         |         |        |
+-------+          +---------+            +---------+         +--------+

1 video source input, 1 channel, 2 lane, 1 remote, 1 video output:
+-------+          +---------+   cable0   +---------+         +--------+
|       | disp in  |         +----------->|         | disp out|        |
|  soc  |--------->|  RK110  |   cable1   |  RK120  +-------->| screen |
|       |          |         +----------->|         |         |        |
+-------+          +---------+            +---------+         +--------+

1 video source input, 2 channel, 2 lane, 2 remote, 2 video output:
                                          +---------+         +--------+
                                  cable0  |         |disp0 out|        |
                                 +------->|  RK120  +-------->| screen |
                                 |        |         |         |        |
+-------+          +---------+   |        +---------+         +--------+
|       | disp in  |         +---+
|  soc  |--------->|  RK110  |
|       |          |         +---+
+-------+          +---------+   |        +---------+         +--------+
                                 |cable1  |         |disp1 out|        |
                                 +------->|  RK120  +-------->| screen |
                                          |         |         |        |
                                          +---------+         +--------+

1 video source input, 2 channel, 1 lane, 1 remote, 2 video output:
                                                              +--------+
                                                     lvds0_tx |        |
                                                         +--->| screen |
+-------+          +---------+            +---------+    |    |        |
|       | disp in  |         |   cable0   |         |----+    +--------+
|  soc  |--------->|  RK110  +----------->|  RK120  |
|       |          |         |            |         |----+    +--------+
+-------+          +---------+            +---------+    |    |        |
                                                         +--->| screen |
                                                     lvds1_tx |        |
                                                              +--------+

2 video source input, 2 channel, 1 lane, 1 remote, 2 video output:
                                                              +--------+
                                                     lvds0_tx |        |
                                                         +--->| screen |
+-------+ disp0_rx +---------+            +---------+    |    |        |
|       |--------->|         |   cable0   |         |----+    +--------+
|  soc  | disp1_rx |  RK110  +----------->|  RK120  |
|       |--------->|         |            |         |----+    +--------+
+-------+          +---------+            +---------+    |    |        |
                                                         +--->| screen |
                                                     lvds1_tx |        |
                                                              +--------+

2 video source input, 2 channel, 2 lane, 1 remote, 2 video output:
                                                              +--------+
                                                     lvds0_tx |        |
                                                         +--->| screen |
+-------+ disp0_rx +---------+   cable0   +---------+    |    |        |
|       |--------->|         +----------->|         |----+    +--------+
|  soc  | disp1_rx |  RK110  |   cable1   |  RK120  |
|       |--------->|         +----------->|         |----+    +--------+
+-------+          +---------+            +---------+    |    |        |
                                                         +--->| screen |
                                                     lvds1_tx |        |
                                                              +--------+

2 video source input, 2 channel, 2 lane, 2 remote, 2 video output:
                                          +---------+         +--------+
                                  cable0  |         |disp0 out|        |
                                 +------->|  RK120  +-------->| screen |
                                 |        |         |         |        |
+-------+ disp0_rx +---------+   |        +---------+         +--------+
|       |--------->|         +---+
|  soc  | disp1_rx |  RK110  |
|       |--------->|         +---+
+-------+          +---------+   |        +---------+         +--------+
                                 |cable1  |         |disp1 out|        |
                                 +------->|  RK120  +-------->| screen |
                                          |         |         |        |
                                          +---------+         +--------+

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Guochun Huang <hero.huang@rock-chips.com>
Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
Signed-off-by: Steven Liu <steven.liu@rock-chips.com>
Change-Id: Ifbf44ff7d5dcab668b50cf101d8dbff10dc71467
This commit is contained in:
Andy Yan
2022-11-22 10:58:50 +08:00
committed by Tao Huang
parent 6904d47493
commit 95f83adcf6
38 changed files with 13427 additions and 0 deletions

View File

@@ -1218,6 +1218,8 @@ config MFD_RK1000
if you say yes here you get support for the RK1000, with func as if you say yes here you get support for the RK1000, with func as
TVEncoder or CODEC. TVEncoder or CODEC.
source "drivers/mfd/rkx110_x120/Kconfig"
config MFD_RN5T618 config MFD_RN5T618
tristate "Ricoh RN5T567/618 PMIC" tristate "Ricoh RN5T567/618 PMIC"
depends on I2C depends on I2C

View File

@@ -230,6 +230,7 @@ obj-$(CONFIG_MFD_RK806) += rk806-core.o
obj-$(CONFIG_MFD_RK806_SPI) += rk806-spi.o obj-$(CONFIG_MFD_RK806_SPI) += rk806-spi.o
obj-$(CONFIG_MFD_RK808) += rk808.o obj-$(CONFIG_MFD_RK808) += rk808.o
obj-$(CONFIG_MFD_RK1000) += rk1000-core.o obj-$(CONFIG_MFD_RK1000) += rk1000-core.o
obj-y += rkx110_x120/
obj-$(CONFIG_MFD_RN5T618) += rn5t618.o obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_SYSCON) += syscon.o

View File

@@ -0,0 +1,18 @@
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
config MFD_RKX110_X120
tristate "Rockchip RKx110 RKx120 SerDes MFD Driver"
depends on I2C
depends on OF
select MFD_CORE
help
if you say yes here you get support for the RKx110 RKx120 from Rockchip.
config ROCKCHIP_SERDES_DRM_PANEL
tristate "Generic RK Serdes panel driver"
depends on OF
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
This driver supports rk serdes panels.

View File

@@ -0,0 +1,23 @@
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
obj-$(CONFIG_MFD_RKX110_X120) += rkx110_x120.o
rkx110_x120-objs := \
hal/cru_core.o \
hal/cru_rkx110.o \
hal/cru_rkx120.o \
hal/cru_rkx111.o \
hal/cru_rkx121.o \
hal/pinctrl_rkx110_x120.o \
pattern_gen.o \
rkx110_combrxphy.o \
rkx110_dsi_rx.o \
rkx110_x120_core.o \
rkx110_linktx.o \
rkx110.o \
rkx120.o \
rkx120_combtxphy.o \
rkx120_dsi_tx.o \
rkx120_linkrx.o \
serdes_combphy.o
obj-$(CONFIG_ROCKCHIP_SERDES_DRM_PANEL) += rkx110_x120_panel.o

View File

@@ -0,0 +1,210 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_API_H_
#define _CRU_API_H_
#include "hal_def.h"
#include "hal_os_def.h"
#include "cru_core.h"
#include "cru_rkx110.h"
#include "cru_rkx120.h"
#include "cru_rkx111.h"
#include "cru_rkx121.h"
static HAL_DEFINE_MUTEX(top_lock);
static inline bool hwclk_is_enabled(struct hwclk *hw, uint32_t clk)
{
bool ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkIsEnabled(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_enable(struct hwclk *hw, uint32_t clk)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkEnable(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_disable(struct hwclk *hw, uint32_t clk)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkDisable(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline bool hwclk_is_reset(struct hwclk *hw, uint32_t clk)
{
bool ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkIsReset(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_reset(struct hwclk *hw, uint32_t clk)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkResetAssert(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_reset_deassert(struct hwclk *hw, uint32_t clk)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkResetDeassert(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_set_div(struct hwclk *hw, uint32_t clk, uint32_t div)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkSetDiv(hw, clk, div);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline uint32_t hwclk_get_div(struct hwclk *hw, uint32_t clk)
{
uint32_t ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkGetDiv(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_set_mux(struct hwclk *hw, uint32_t clk, uint32_t mux)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkSetMux(hw, clk, mux);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline uint32_t hwclk_get_mux(struct hwclk *hw, uint32_t clk)
{
uint32_t ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkGetMux(hw, clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline uint32_t hwclk_get_rate(struct hwclk *hw, uint32_t composite_clk)
{
uint32_t ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkGetFreq(hw, composite_clk);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_set_rate(struct hwclk *hw, uint32_t composite_clk, uint32_t rate)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkSetFreq(hw, composite_clk, rate);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline int hwclk_set_testout(struct hwclk *hw, uint32_t composite_clk,
uint32_t mux, uint32_t div)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_ClkSetTestout(hw, composite_clk, mux, div);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline void hwclk_dump_tree(HAL_ClockType type)
{
HAL_MutexLock(&top_lock);
HAL_CRU_ClkDumpTree(type);
HAL_MutexUnlock(&top_lock);
}
static inline int hwclk_set_glbsrst(struct hwclk *hw, uint32_t type)
{
int ret;
HAL_MutexLock(&hw->lock);
ret = HAL_CRU_SetGlbSrst(hw, type);
HAL_MutexUnlock(&hw->lock);
return ret;
}
static inline struct hwclk *hwclk_register(struct xferclk xfer)
{
struct hwclk *ret;
HAL_MutexLock(&top_lock);
ret = HAL_CRU_Register(xfer);
HAL_MutexUnlock(&top_lock);
return ret;
}
static inline struct hwclk *hwclk_get(void *client)
{
struct hwclk *ret;
HAL_MutexLock(&top_lock);
ret = HAL_CRU_ClkGet(client);
HAL_MutexUnlock(&top_lock);
return ret;
}
static inline int hwclk_init(void)
{
return HAL_CRU_Init();
}
#endif

View File

@@ -0,0 +1,904 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#include "cru_core.h"
/********************* Private MACRO Definition ******************************/
#define REG_X4(i) ((i) * 4)
#define PWRDOWN_SHIFT 13
#define PWRDOWN_MASK (1 << PWRDOWN_SHIFT)
#define PLL_POSTDIV1_SHIFT 12
#define PLL_POSTDIV1_MASK (0x7 << PLL_POSTDIV1_SHIFT)
#define PLL_FBDIV_SHIFT 0
#define PLL_FBDIV_MASK (0xfff << PLL_FBDIV_SHIFT)
#define PLL_POSTDIV2_SHIFT 6
#define PLL_POSTDIV2_MASK (0x7 << PLL_POSTDIV2_SHIFT)
#define PLL_REFDIV_SHIFT 0
#define PLL_REFDIV_MASK (0x3f << PLL_REFDIV_SHIFT)
#define PLL_DSMPD_SHIFT 12
#define PLL_DSMPD_MASK (1 << PLL_DSMPD_SHIFT)
#define PLL_FRAC_SHIFT 0
#define PLL_FRAC_MASK (0xffffff << PLL_FRAC_SHIFT)
#define EXPONENT_OF_FRAC_PLL 24
#define RK_PLL_MODE_SLOW 0
#define RK_PLL_MODE_NORMAL 1
#define RK_PLL_MODE_DEEP 2
#define MHZ 1000000
#define PLL_GET_PLLMODE(val, shift, mask) \
(((uint32_t)(val) & mask) >> shift)
#define PLL_GET_FBDIV(x) \
(((uint32_t)(x) & (PLL_FBDIV_MASK)) >> PLL_FBDIV_SHIFT)
#define PLL_GET_REFDIV(x) \
(((uint32_t)(x) & (PLL_REFDIV_MASK)) >> PLL_REFDIV_SHIFT)
#define PLL_GET_POSTDIV1(x) \
(((uint32_t)(x) & (PLL_POSTDIV1_MASK)) >> PLL_POSTDIV1_SHIFT)
#define PLL_GET_POSTDIV2(x) \
(((uint32_t)(x) & (PLL_POSTDIV2_MASK)) >> PLL_POSTDIV2_SHIFT)
#define PLL_GET_DSMPD(x) \
(((uint32_t)(x) & (PLL_DSMPD_MASK)) >> PLL_DSMPD_SHIFT)
#define PLL_GET_FRAC(x) \
(((uint32_t)(x) & (PLL_FRAC_MASK)) >> PLL_FRAC_SHIFT)
#define CRU_PLL_ROUND_UP_TO_KHZ(x) \
(HAL_DIV_ROUND_UP((x), 1000) * 1000)
static struct hwclk g_hwclk_desc[HAL_HWCLK_MAX_NUM];
static struct PLL_CONFIG g_AutoTable;
/********************* Private Function Definition ***************************/
uint32_t HAL_CRU_Read(struct hwclk *hw, uint32_t reg)
{
uint32_t val;
int ret;
ret = hw->xfer.read(hw->xfer.client, reg, &val);
if (ret) {
CRU_ERR("%s: read reg=0x%08x failed, ret=%d\n", hw->name, reg, ret);
}
CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
return val;
}
uint32_t HAL_CRU_Write(struct hwclk *hw, uint32_t reg, uint32_t val)
{
int ret;
CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
ret = hw->xfer.write(hw->xfer.client, reg, val);
if (ret) {
CRU_ERR("%s: write reg=0x%08x, val=0x%08x failed, ret=%d\n",
hw->name, reg, val, ret);
}
return ret;
}
uint32_t HAL_CRU_WriteMask(struct hwclk *hw, uint32_t reg,
uint32_t msk, uint32_t val)
{
return HAL_CRU_Write(hw, reg, VAL_MASK_WE(msk, val));
}
static int isBetterFreq(uint32_t now, uint32_t new, uint32_t best)
{
return (new <= now && new > best);
}
int HAL_CRU_FreqGetMux4(struct hwclk *hw,
uint32_t freq, uint32_t freq0,
uint32_t freq1, uint32_t freq2, uint32_t freq3)
{
uint32_t best = 0;
if (isBetterFreq(freq, freq0, best)) {
best = freq0;
}
if (isBetterFreq(freq, freq1, best)) {
best = freq1;
}
if (isBetterFreq(freq, freq2, best)) {
best = freq2;
}
if (isBetterFreq(freq, freq3, best)) {
best = freq3;
}
if (best == freq0) {
return 0;
} else if (best == freq1) {
return 1;
} else if (best == freq2) {
return 2;
} else if (best == freq3) {
return 3;
}
return HAL_INVAL;
}
int HAL_CRU_FreqGetMux3(struct hwclk *hw,
uint32_t freq, uint32_t freq0,
uint32_t freq1, uint32_t freq2)
{
return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq2, freq2);
}
int HAL_CRU_FreqGetMux2(struct hwclk *hw,
uint32_t freq, uint32_t freq0, uint32_t freq1)
{
return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq1, freq1);
}
uint32_t HAL_CRU_MuxGetFreq4(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1,
uint32_t freq2, uint32_t freq3)
{
switch (HAL_CRU_ClkGetMux(hw, muxName)) {
case 0:
return freq0;
case 1:
return freq1;
case 2:
return freq2;
case 3:
return freq3;
}
return HAL_INVAL;
}
uint32_t HAL_CRU_MuxGetFreq3(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1, uint32_t freq2)
{
return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq2, freq2);
}
uint32_t HAL_CRU_MuxGetFreq2(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1)
{
return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq1, freq1);
}
int HAL_CRU_RoundFreqGetMux4(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1,
uint32_t pFreq2, uint32_t pFreq3, uint32_t *pFreqOut)
{
uint32_t mux;
if (pFreq3 && (pFreq3 % freq == 0)) {
*pFreqOut = pFreq3;
mux = 3;
} else if (pFreq2 && (pFreq2 % freq == 0)) {
*pFreqOut = pFreq2;
mux = 2;
} else if (pFreq1 % freq == 0) {
*pFreqOut = pFreq1;
mux = 1;
} else {
*pFreqOut = pFreq0;
mux = 0;
}
return mux;
}
int HAL_CRU_RoundFreqGetMux3(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1,
uint32_t pFreq2, uint32_t *pFreqOut)
{
return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, pFreq2, 0, pFreqOut);
}
int HAL_CRU_RoundFreqGetMux2(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1, uint32_t *pFreqOut)
{
return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, 0, 0, pFreqOut);
}
/******************************************************************************/
uint32_t HAL_CRU_GetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup)
{
uint32_t refDiv, fbDiv, postdDv1, postDiv2, frac, dsmpd;
uint32_t mode = 0, rate = OSC_24M;
mode = PLL_GET_PLLMODE(HAL_CRU_Read(hw, pSetup->modeOffset),
pSetup->modeShift,
pSetup->modeMask);
switch (mode) {
case RK_PLL_MODE_SLOW:
rate = OSC_24M;
break;
case RK_PLL_MODE_NORMAL:
postdDv1 = PLL_GET_POSTDIV1(HAL_CRU_Read(hw, pSetup->conOffset0));
fbDiv = PLL_GET_FBDIV(HAL_CRU_Read(hw, pSetup->conOffset0));
postDiv2 = PLL_GET_POSTDIV2(HAL_CRU_Read(hw, pSetup->conOffset1));
refDiv = PLL_GET_REFDIV(HAL_CRU_Read(hw, pSetup->conOffset1));
dsmpd = PLL_GET_DSMPD(HAL_CRU_Read(hw, pSetup->conOffset1));
frac = PLL_GET_FRAC(HAL_CRU_Read(hw, pSetup->conOffset2));
rate = (rate / refDiv) * fbDiv;
if (dsmpd == 0) {
uint64_t fracRate = OSC_24M;
fracRate *= frac;
fracRate = fracRate >> EXPONENT_OF_FRAC_PLL;
fracRate = fracRate / refDiv;
rate += fracRate;
}
rate = rate / (postdDv1 * postDiv2);
rate = CRU_PLL_ROUND_UP_TO_KHZ(rate);
break;
case RK_PLL_MODE_DEEP:
default:
rate = 32768;
break;
}
return rate;
}
static uint32_t CRU_Gcd(uint32_t m, uint32_t n)
{
int t;
while (m > 0) {
if (n > m) {
t = m;
m = n;
n = t;
}
m -= n;
}
return n;
}
static uint64_t HAL_DivU64Rem(uint64_t numerator, uint32_t denominator, uint32_t *pRemainder)
{
uint64_t remainder = numerator;
uint64_t b = denominator;
uint64_t result;
uint64_t d = 1;
uint32_t high = numerator >> 32;
result = 0;
if (high >= denominator) {
high /= denominator;
result = (uint64_t)high << 32;
remainder -= (uint64_t)(high * denominator) << 32;
}
while ((int64_t)b > 0 && b < remainder) {
b = b + b;
d = d + d;
}
do {
if (remainder >= b) {
remainder -= b;
result += d;
}
b >>= 1;
d >>= 1;
} while (d);
if (pRemainder) {
*pRemainder = remainder;
}
return result;
}
static inline uint64_t HAL_DivU64(uint64_t numerator, uint32_t denominator)
{
return HAL_DivU64Rem(numerator, denominator, HAL_NULL);
}
static const struct PLL_CONFIG *CRU_PllSetByAuto(struct hwclk *hw,
struct PLL_SETUP *pSetup,
uint32_t finHz,
uint32_t foutHz)
{
struct PLL_CONFIG *rateTable = &g_AutoTable;
uint32_t refDiv, fbDiv, dsmpd;
uint32_t postDiv1, postDiv2;
uint32_t clkGcd = 0;
uint64_t foutVco;
uint64_t intVco;
uint64_t frac64;
CRU_DBGF("%s: %s: pll=%d, refdiv: [%u, %u], foutVco: [%u, %u], fout: [%u, %u]\n",
__func__, hw->name, pSetup->id, pSetup->minRefdiv, pSetup->maxRefdiv,
pSetup->minVco, pSetup->maxVco, pSetup->minFout, pSetup->maxFout);
if (finHz == 0 || foutHz == 0 || foutHz == finHz) {
return HAL_NULL;
}
if ((foutHz < pSetup->minFout) || (foutHz > pSetup->maxFout)) {
return HAL_NULL;
}
for (postDiv1 = 1; postDiv1 <= 7; postDiv1++) {
for (postDiv2 = 1; postDiv2 <= 7; postDiv2++) {
if (postDiv1 < postDiv2) {
CRU_DBGF("%s: %s: pll=%d, Invalid postDiv1(%u) < postDiv2(%u)\n",
__func__, hw->name, pSetup->id, postDiv1, postDiv2);
continue;
}
foutVco = (uint64_t)foutHz * postDiv1 * postDiv2;
if ((foutVco < pSetup->minVco) || (foutVco > pSetup->maxVco)) {
CRU_DBGF("%s: %s: pll=%d, Invalid foutVco(%llu), min_max[%u, %u]\n",
__func__, hw->name, pSetup->id, foutVco, pSetup->minVco, pSetup->maxVco);
continue;
}
intVco = foutVco / MHZ * MHZ;
clkGcd = CRU_Gcd(finHz, intVco);
refDiv = finHz / clkGcd;
fbDiv = intVco / clkGcd;
if ((refDiv < pSetup->minRefdiv) || (refDiv > pSetup->maxRefdiv)) {
CRU_DBGF("%s: %s: pll=%d, Invalid refDiv(%u), min_max[%u, %u]\n",
__func__, hw->name, pSetup->id, refDiv, pSetup->minRefdiv, pSetup->maxRefdiv);
continue;
}
if (foutHz / MHZ * MHZ == foutHz) {
dsmpd = 1;
frac64 = 0;
} else {
frac64 = (foutVco % MHZ) * refDiv;
frac64 = HAL_DivU64(frac64 << EXPONENT_OF_FRAC_PLL, OSC_24M);
if (frac64 > 0) {
dsmpd = 0;
} else {
dsmpd = 1;
}
}
if (dsmpd && !(fbDiv >= 16 && fbDiv <= 2500)) {
CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on int mode, min_max[16, 2500]\n",
__func__, hw->name, pSetup->id, fbDiv);
continue;
}
if (!dsmpd && !(fbDiv >= 20 && fbDiv <= 500)) {
CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on frac mode, min_max[20, 500]\n",
__func__, hw->name, pSetup->id, fbDiv);
continue;
}
if (frac64 > 0x00ffffff) {
CRU_DBGF("%s: %s: pll=%d, Invalid frac64(0x%llx) over max 0x00ffffff\n",
__func__, hw->name, pSetup->id, frac64);
continue;
}
rateTable->refDiv = refDiv;
rateTable->fbDiv = fbDiv;
rateTable->postDiv1 = postDiv1;
rateTable->postDiv2 = postDiv2;
rateTable->dsmpd = dsmpd;
rateTable->frac = frac64;
return rateTable;
}
}
return HAL_NULL;
}
static const struct PLL_CONFIG *CRU_PllGetSettings(struct hwclk *hw,
struct PLL_SETUP *pSetup,
uint32_t rate)
{
const struct PLL_CONFIG *rateTable = pSetup->rateTable;
if (!rateTable) {
CRU_ERR("%s: %s: Unavailable pll=%d rate table\n", __func__, hw->name, pSetup->id);
return HAL_NULL;
}
while (rateTable->rate) {
if (rateTable->rate == rate) {
return rateTable;
}
rateTable++;
}
rateTable = CRU_PllSetByAuto(hw, pSetup, OSC_24M, rate);
if (!rateTable) {
CRU_ERR("%s: %s: Unsupported pll=%d rate %d\n", __func__, hw->name, pSetup->id, rate);
return HAL_NULL;
} else {
CRU_MSG("%s: Auto PLL=%d: (%d, %d, %d, %d, %d, %d, %d)\n",
hw->name, pSetup->id, rate, rateTable->refDiv, rateTable->fbDiv,
rateTable->postDiv1, rateTable->postDiv2,
rateTable->dsmpd, rateTable->frac);
}
return rateTable;
}
/*
* Force PLL into slow mode
* Pll Power down
* Pll Config fbDiv, refDiv, postdDv1, postDiv2, dsmpd, frac
* Pll Power up
* Waiting for pll lock
* Force PLL into normal mode
*/
HAL_Status HAL_CRU_SetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate)
{
const struct PLL_CONFIG *pConfig;
int delay = 2400;
if (rate == HAL_CRU_GetPllFreq(hw, pSetup)) {
return HAL_OK;
}
pConfig = CRU_PllGetSettings(hw, pSetup, rate);
if (!pConfig) {
return HAL_ERROR;
}
/* Force PLL into slow mode to ensure output stable clock */
HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_SLOW << pSetup->modeShift);
/* Pll Power down */
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 1 << PWRDOWN_SHIFT);
/* Pll Config */
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_POSTDIV2_MASK, pConfig->postDiv2 << PLL_POSTDIV2_SHIFT);
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_REFDIV_MASK, pConfig->refDiv << PLL_REFDIV_SHIFT);
HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_POSTDIV1_MASK, pConfig->postDiv1 << PLL_POSTDIV1_SHIFT);
HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_FBDIV_MASK, pConfig->fbDiv << PLL_FBDIV_SHIFT);
if (pSetup->sscgEn) {
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, 0 << PLL_DSMPD_SHIFT);
} else {
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, pConfig->dsmpd << PLL_DSMPD_SHIFT);
}
if (pConfig->frac) {
HAL_CRU_Write(hw, pSetup->conOffset2, (HAL_CRU_Read(hw, pSetup->conOffset2) & 0xff000000) | pConfig->frac);
}
/* Pll Power up */
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT);
/* Waiting for pll lock */
while (delay > 0) {
if (pSetup->stat0) {
if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) {
break;
}
} else {
if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) {
break;
}
}
HAL_DelayUs(2);
delay--;
}
if (delay == 0) {
return HAL_TIMEOUT;
}
/* Force PLL into normal mode */
HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_NORMAL << pSetup->modeShift);
return HAL_OK;
}
HAL_Status HAL_CRU_SetPllPowerUp(struct hwclk *hw, struct PLL_SETUP *pSetup)
{
int delay = 2400;
/* Pll Power up */
HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT);
/* Waiting for pll lock */
while (delay > 0) {
if (pSetup->stat0) {
if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) {
break;
}
} else {
if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) {
break;
}
}
HAL_DelayUs(1000);
delay--;
}
if (delay == 0) {
return HAL_TIMEOUT;
}
return HAL_OK;
}
HAL_Status HAL_CRU_SetPllPowerDown(struct hwclk *hw, struct PLL_SETUP *pSetup)
{
HAL_CRU_Write(hw, pSetup->conOffset1, VAL_MASK_WE(PWRDOWN_MASK, 1 << PWRDOWN_SHIFT));
return HAL_OK;
}
HAL_Check HAL_CRU_ClkIsEnabled(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
return (HAL_Check)((HAL_CRU_Read(hw, hw->gate_con0 + REG_X4(index)) & (1 << shift)) >> shift);
}
HAL_Status HAL_CRU_ClkEnable(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
uint32_t gid = 16 * index + shift;
if (gid >= hw->num_gate) {
CRU_ERR("%s: %s: clock(#%08x) is unsupported, gid=%d\n", __func__, hw->name, clk, gid);
return HAL_INVAL;
}
if (hw->gate[gid].enable_count == 0) {
CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid);
HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift));
}
hw->gate[gid].enable_count++;
return HAL_OK;
}
HAL_Status HAL_CRU_ClkDisable(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
uint32_t gid = 16 * index + shift;
if (gid >= hw->num_gate) {
CRU_ERR("%s: %s: clock(#%08x) is unsupported\n", __func__, hw->name, clk);
return HAL_INVAL;
}
if (hw->gate[gid].enable_count == 0) {
CRU_ERR("%s: %s: clock(#%08x) is already disabled\n", __func__, hw->name, clk);
return HAL_OK;
}
hw->gate[gid].enable_count--;
if (hw->gate[gid].enable_count == 0) {
CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid);
HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift));
}
return HAL_OK;
}
HAL_Check HAL_CRU_ClkIsReset(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
return (HAL_Check)((HAL_CRU_Read(hw, hw->softrst_con0 + REG_X4(index)) & (1 << shift)) >> shift);
}
HAL_Status HAL_CRU_ClkResetAssert(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift));
return HAL_OK;
}
HAL_Status HAL_CRU_ClkResetDeassert(struct hwclk *hw, uint32_t clk)
{
uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift));
return HAL_OK;
}
HAL_Status HAL_CRU_ClkSetDiv(struct hwclk *hw, uint32_t divName, uint32_t divValue)
{
uint32_t shift, mask, index;
index = CLK_DIV_GET_REG_OFFSET(divName);
shift = CLK_DIV_GET_BITS_SHIFT(divName);
mask = CLK_DIV_GET_MASK(divName);
if (divValue > mask) {
divValue = mask;
}
CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, div=%d\n",
__func__, hw->name, divName, index, hw->sel_con0 + REG_X4(index), shift, mask, divValue);
HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, (divValue - 1U) << shift));
return HAL_OK;
}
uint32_t HAL_CRU_ClkGetDiv(struct hwclk *hw, uint32_t divName)
{
uint32_t shift, mask, index, divValue;
index = CLK_DIV_GET_REG_OFFSET(divName);
shift = CLK_DIV_GET_BITS_SHIFT(divName);
mask = CLK_DIV_GET_MASK(divName);
divValue = ((HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift) + 1;
return divValue;
}
HAL_Status HAL_CRU_ClkSetMux(struct hwclk *hw, uint32_t muxName, uint32_t muxValue)
{
uint32_t shift, mask, index;
index = CLK_MUX_GET_REG_OFFSET(muxName);
shift = CLK_MUX_GET_BITS_SHIFT(muxName);
mask = CLK_MUX_GET_MASK(muxName);
CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, mux=%d\n",
__func__, hw->name, muxName, index, hw->sel_con0 + REG_X4(index), shift, mask, muxValue);
HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, muxValue << shift));
return HAL_OK;
}
uint32_t HAL_CRU_ClkGetMux(struct hwclk *hw, uint32_t muxName)
{
uint32_t shift, mask, index, muxValue;
index = CLK_MUX_GET_REG_OFFSET(muxName);
shift = CLK_MUX_GET_BITS_SHIFT(muxName);
mask = CLK_MUX_GET_MASK(muxName);
muxValue = (HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift;
return muxValue;
}
HAL_Status HAL_CRU_ClkSetTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue)
{
if (hw->ops->clkInitTestout) {
hw->ops->clkInitTestout(hw, clockName, muxValue, divValue);
}
return HAL_OK;
}
void HAL_CRU_ClkDumpTree(HAL_ClockType type)
{
struct hwclk *hw = &g_hwclk_desc[0];
struct clkTable *c;
const char *parent;
uint32_t clkMux, mux;
uint32_t i, freq;
for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) {
if (hw->type == CLK_UNDEF) {
break;
}
if ((type != hw->type) && (type != CLK_ALL)) {
continue;
}
if (!hw->ops->clkTable) {
continue;
}
CRU_MSG(" ================== %s ==================\n", hw->name);
CRU_MSG(" Name Rate:hz Parent\n");
CRU_MSG(" ====================================================\n");
for (c = hw->ops->clkTable; c->name; c++) {
if (c->type == DUMP_INT) {
if (c->numParents == 1) {
mux = 0;
} else {
clkMux = CLK_GET_MUX(c->clk);
mux = HAL_CRU_ClkGetMux(hw, clkMux);
}
freq = HAL_CRU_ClkGetFreq(hw, c->clk);
parent = c->parents[mux];
} else if (c->type == DUMP_EXT) {
freq = c->getFreq(hw, c->clk);
parent = c->extParent ? c->extParent : "ext-in";
} else {
continue;
}
CRU_MSG(" %-30s %10d %s\n", c->name, freq, parent);
}
CRU_MSG("\n");
}
}
HAL_Status HAL_CRU_SetGlbSrst(struct hwclk *hw, eCRU_GlbSrstType type)
{
if (type == GLB_SRST_FST) {
HAL_CRU_Write(hw, hw->gbl_srst_fst, GLB_SRST_FST);
}
return HAL_INVAL;
}
uint32_t HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
{
uint32_t rate;
rate = hw->ops->clkGetFreq(hw, clockName);
CRU_DBGF("%s: %s: clock(#%08x) get rate: %d\n", __func__, hw->name, clockName, rate);
return rate;
}
HAL_Status HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
{
HAL_Status ret;
uint32_t now;
CRU_DBGF("%s: %s: clock(#%08x) set rate: %d\n", __func__, hw->name, clockName, rate);
ret = hw->ops->clkSetFreq(hw, clockName, rate);
if (hw->flags & CLK_FLG_SET_RATE_VERIFY) {
now = hw->ops->clkGetFreq(hw, clockName);
if (rate != now) {
CRU_MSG("%s: Set rate %d, but %d returned\n",
hw->name, rate, now);
ret = HAL_ERROR;
}
}
return ret;
}
HAL_Status HAL_CRU_Init(void)
{
return HAL_OK;
}
struct hwclk *HAL_CRU_ClkGet(void *client)
{
struct hwclk *hw = &g_hwclk_desc[0];
int i;
if (!client) {
return HAL_NULL;
}
for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) {
if (hw->xfer.client == client) {
return hw;
}
}
return HAL_NULL;
}
struct hwclk *HAL_CRU_Register(struct xferclk xfer)
{
struct hwclk hw;
int i, ret;
if (!xfer.read || !xfer.write || !xfer.client || !xfer.name[0]) {
return HAL_NULL;
}
if (xfer.type == CLK_UNDEF || xfer.type >= CLK_MAX) {
return HAL_NULL;
}
/* registered ? */
for (i = 0; i < HAL_HWCLK_MAX_NUM; i++) {
if (g_hwclk_desc[i].type == CLK_UNDEF) {
break;
}
if (g_hwclk_desc[i].xfer.client == xfer.client) {
return &g_hwclk_desc[i];
}
}
if (i >= HAL_HWCLK_MAX_NUM) {
CRU_ERR("CLK: No more space for register\n");
return HAL_NULL;
}
memset(&hw, 0, sizeof(hw));
hw.type = xfer.type;
hw.xfer = xfer;
switch (xfer.type) {
case CLK_RKX110:
if (xfer.version == 0) {
hw.ops = &rkx110_clk_ops;
} else if (xfer.version == 1) {
hw.ops = &rkx111_clk_ops;
}
break;
case CLK_RKX120:
if (xfer.version == 0) {
hw.ops = &rkx120_clk_ops;
} else if (xfer.version == 1) {
hw.ops = &rkx121_clk_ops;
}
break;
default:
CRU_ERR("%s: Invalid type: %d\n", __func__, xfer.type);
return HAL_NULL;
}
if (!hw.ops || !hw.ops->clkInit || !hw.ops->clkGetFreq || !hw.ops->clkSetFreq) {
CRU_ERR("No available clkOps or .clkInit() or .clkGetFreq() or .clkSetFreq()\n");
return HAL_NULL;
}
HAL_MutexInit(&hw.lock);
ret = hw.ops->clkInit(&hw, &xfer);
if (ret) {
CRU_ERR("%s: Init clock failed, ret=%d\n", hw.name, ret);
return HAL_NULL;
}
if (!hw.num_gate) {
CRU_ERR("No available .gate\n");
return HAL_NULL;
}
#if HAL_ENABLE_SET_RATE_VERIFY
hw->flags |= CLK_FLG_SET_RATE_VERIFY;
#endif
g_hwclk_desc[i] = hw;
CRU_MSG("CLK: register '%s' with client 0x%08lx successfully.\n",
hw.name, (unsigned long)hw.xfer.client);
#if DEBUG_CRU_INIT
HAL_CRU_ClkDumpTree(xfer.type);
#endif
return &g_hwclk_desc[i];
}

View File

@@ -0,0 +1,450 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_CORE_H_
#define _CRU_CORE_H_
#include "hal_def.h"
#include "hal_os_def.h"
#define DEBUG_CRU_INIT 0
#define CRU_LOGLEVEL 3
#define __cru_print(level, fmt, ...) \
({ \
level < CRU_LOGLEVEL ? HAL_SYSLOG("[HAL CRU] " fmt, ##__VA_ARGS__) : 0; \
})
#define CRU_ERR(fmt, ...) __cru_print(0, "ERROR: " fmt, ##__VA_ARGS__)
#define CRU_WARN(fmt, ...) __cru_print(1, "WARN: " fmt, ##__VA_ARGS__)
#define CRU_MSG(fmt, ...) __cru_print(2, fmt, ##__VA_ARGS__)
#define CRU_DBG(fmt, ...) __cru_print(3, fmt, ##__VA_ARGS__) /* driver */
#define CRU_DBGF(fmt, ...) __cru_print(4, fmt, ##__VA_ARGS__) /* core function */
#define CRU_DBGR(fmt, ...) __cru_print(5, fmt, ##__VA_ARGS__) /* core register */
#define HAL_ENABLE_SET_RATE_VERIFY 0
#define _MHZ(n) ((n) * 1000000U)
#define OSC_24M _MHZ(24)
#define HAL_HWCLK_MAX_NUM 16
#define PLL_MAX_NUM 2
typedef enum {
RKX110_CPLL = 0,
RKX110_RXPLL,
RKX120_CPLL = 0,
RKX120_TXPLL,
} HAL_PLLType;
typedef enum {
CLK_UNDEF,
CLK_RKX110,
CLK_RKX120,
CLK_ALL,
CLK_MAX,
} HAL_ClockType;
/*
* hwclk flags
*/
#define CLK_FLG_SET_RATE_VERIFY (1 << 0) /* set and readback verify */
struct hwclk;
struct xferclk {
HAL_ClockType type;
u32 version;
char *name; /* slave addr is expected */
void *client;
HAL_RegRead_t *read;
HAL_RegWrite_t *write;
};
struct clkTable {
uint8_t type;
const char *name;
uint32_t clk;
const char * *parents;
uint32_t numParents;
const char *extParent;
uint32_t (*getFreq)(struct hwclk *hw, uint32_t clockName);
};
struct clkOps {
struct clkTable *clkTable;
HAL_Status (*clkInit)(struct hwclk *hw, struct xferclk *xfer);
HAL_Status (*clkInitTestout)(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue);
HAL_Status (*clkSetFreq)(struct hwclk *hw, uint32_t clockName, uint32_t rate);
uint32_t (*clkGetFreq)(struct hwclk *hw, uint32_t clockName);
};
struct clkGate {
uint8_t enable_count;
};
struct hwclk {
char name[32];
uint32_t flags;
HAL_ClockType type;
uint32_t pllRate[PLL_MAX_NUM];
uint32_t cru_base;
uint32_t gate_con0;
uint32_t sel_con0;
uint32_t softrst_con0;
uint32_t gbl_srst_fst;
struct xferclk xfer;
struct clkOps *ops;
struct clkGate *gate;
uint32_t num_gate;
HAL_Mutex lock;
};
#define MASK_TO_WE(msk) ((msk) << 16)
#define VAL_MASK_WE(msk, val) ((MASK_TO_WE(msk)) | (val))
#define WRITE_REG_MASK_WE(reg, msk, val) \
WRITE_REG(reg, (VAL_MASK_WE(msk, val)))
#define _GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (HAL_BITS_PER_LONG - 1 - (h))))
#define _GENVAL(x, h, l) ((uint32_t)(((x) & _GENMASK(h, l)) >> (l)))
#define _GENVAL_D16(x, h, l) ((uint32_t)(((x) & _GENMASK(h, l)) / 16))
#define _GENVAL_D16_REM(x, h, l) ((uint32_t)(((x) & _GENMASK(h, l)) % 16))
#define _WIDTH_TO_MASK(w) ((1 << (w)) - 1)
/*
* RESET/GATE fields:
* [31:16]: reserved
* [15:12]: bank
* [11:0]: id
*/
#define CLK_RESET_GET_REG_OFFSET(x) _GENVAL_D16(x, 11, 0)
#define CLK_RESET_GET_BITS_SHIFT(x) _GENVAL_D16_REM(x, 11, 0)
#define CLK_RESET_GET_REG_BANK(x) _GENVAL(x, 15, 12)
#define CLK_GATE_GET_REG_OFFSET(x) CLK_RESET_GET_REG_OFFSET(x)
#define CLK_GATE_GET_BITS_SHIFT(x) CLK_RESET_GET_BITS_SHIFT(x)
#define CLK_GATE_GET_REG_BANK(x) CLK_RESET_GET_REG_BANK(x)
/*
* MUX/DIV fields:
* [31:24]: width
* [23:16]: shift
* [15:12]: reserved
* [11:8]: bank
* [7:0]: reg
*/
#define CLK_MUX_GET_REG_OFFSET(x) _GENVAL(x, 7, 0)
#define CLK_MUX_GET_BANK(x) _GENVAL(x, 11, 8)
#define CLK_MUX_GET_BITS_SHIFT(x) _GENVAL(x, 23, 16)
#define CLK_MUX_GET_WIDTH(x) _GENVAL(x, 31, 24)
#define CLK_MUX_GET_MASK(x) (_WIDTH_TO_MASK(CLK_MUX_GET_WIDTH(x)) << CLK_MUX_GET_BITS_SHIFT(x))
#define CLK_DIV_GET_REG_OFFSET(x) CLK_MUX_GET_REG_OFFSET(x)
#define CLK_DIV_GET_BANK(x) CLK_MUX_GET_BANK(x)
#define CLK_DIV_GET_BITS_SHIFT(x) CLK_MUX_GET_BITS_SHIFT(x)
#define CLK_DIV_GET_WIDTH(x) CLK_MUX_GET_WIDTH(x)
#define CLK_DIV_GET_MASK(x) CLK_MUX_GET_MASK(x)
#define CLK_DIV_GET_MAXDIV(x) ((1 << CLK_DIV_GET_WIDTH(x)) - 1)
#define CLK_GET_MUX(v32) \
((uint32_t)((v32) & 0x0F0F00FFU))
#define CLK_GET_DIV(v32) \
((uint32_t)((((v32) & 0x0000FF00U) >> 8) | \
(((v32) & 0xF0F00000U) >> 4)))
#define COMPOSITE_CLK(mux, div) \
(((mux) & 0x0F0F00FFU) | (((div) & 0xFFU) << 8) | \
(((div) & 0x0F0F0000U) << 4))
#define PNAME(x) static const char *x[]
typedef enum {
DUMP_UNDEF,
DUMP_INT,
DUMP_EXT,
} HAL_DumpType;
#define CLK_DECLARE(_type, _name, _clk, _parents, _numParents, _extParent, _getFreq) \
{ \
.type = _type, \
.name = _name, \
.clk = _clk, \
.parents = _parents, \
.numParents = _numParents, \
.extParent = _extParent, \
.getFreq = _getFreq, \
}
#define CLK_DECLARE_INT(_name, _clk, _parents) \
CLK_DECLARE(DUMP_INT, _name, _clk, _parents, HAL_ARRAY_SIZE(_parents), HAL_NULL, HAL_NULL)
#define CLK_DECLARE_EXT_PARENT(_name, _clk, _extParent, _getFreq) \
CLK_DECLARE(DUMP_EXT, _name, _clk, HAL_NULL, 0, _extParent, _getFreq)
#define CLK_DECLARE_EXT(_name, _clk, _getFreq) \
CLK_DECLARE_EXT_PARENT(_name, _clk, HAL_NULL, _getFreq)
#define RK_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac) \
{ \
.rate = _rate##U, .fbDiv = _fbdiv, .postDiv1 = _postdiv1, \
.refDiv = _refdiv, .postDiv2 = _postdiv2, .dsmpd = _dsmpd, \
.frac = _frac, \
}
#define DIV_NO_REM(pFreq, freq, maxDiv) \
((!((pFreq) % (freq))) && ((pFreq) / (freq) <= (maxDiv)))
#define HAL_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
/***************************** Structure Definition **************************/
struct PLL_CONFIG {
uint32_t rate;
union {
struct {
uint32_t fbDiv;
uint32_t postDiv1;
uint32_t refDiv;
uint32_t postDiv2;
uint32_t dsmpd;
uint32_t frac;
};
};
};
typedef enum {
PLL_CPLL,
PLL_RXPLL,
PLL_TXPLL,
} eCRU_PLL;
struct PLL_SETUP {
eCRU_PLL id;
uint32_t conOffset0;
uint32_t conOffset1;
uint32_t conOffset2;
uint32_t conOffset3;
uint32_t conOffset6;
uint32_t modeOffset;
uint32_t stat0;
uint32_t modeShift;
uint32_t lockShift;
uint32_t modeMask;
const struct PLL_CONFIG *rateTable;
uint8_t minRefdiv;
uint8_t maxRefdiv;
uint32_t minVco;
uint32_t maxVco;
uint32_t minFout;
uint32_t maxFout;
uint8_t sscgEn;
};
typedef enum {
GLB_SRST_FST = 0xfdb9,
GLB_SRST_SND = 0xeca8,
} eCRU_GlbSrstType;
/***************************** Function Declare ******************************/
uint32_t HAL_CRU_Read(struct hwclk *hw, uint32_t reg);
uint32_t HAL_CRU_Write(struct hwclk *hw, uint32_t reg, uint32_t val);
uint32_t HAL_CRU_WriteMask(struct hwclk *hw, uint32_t reg,
uint32_t msk, uint32_t val);
int HAL_CRU_FreqGetMux4(struct hwclk *hw,
uint32_t freq, uint32_t freq0,
uint32_t freq1, uint32_t freq2, uint32_t freq3);
int HAL_CRU_FreqGetMux3(struct hwclk *hw,
uint32_t freq, uint32_t freq0,
uint32_t freq1, uint32_t freq2);
int HAL_CRU_FreqGetMux2(struct hwclk *hw,
uint32_t freq, uint32_t freq0, uint32_t freq1);
uint32_t HAL_CRU_MuxGetFreq4(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1,
uint32_t freq2, uint32_t freq3);
uint32_t HAL_CRU_MuxGetFreq3(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1, uint32_t freq2);
uint32_t HAL_CRU_MuxGetFreq2(struct hwclk *hw, uint32_t muxName,
uint32_t freq0, uint32_t freq1);
int HAL_CRU_RoundFreqGetMux4(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1,
uint32_t pFreq2, uint32_t pFreq3, uint32_t *pFreqOut);
int HAL_CRU_RoundFreqGetMux3(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1,
uint32_t pFreq2, uint32_t *pFreqOut);
int HAL_CRU_RoundFreqGetMux2(struct hwclk *hw, uint32_t freq,
uint32_t pFreq0, uint32_t pFreq1, uint32_t *pFreqOut);
/**
* @brief Get pll freq.
* @param pSetup: Contains PLL register parameters
* @return pll rate.
*/
uint32_t HAL_CRU_GetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup);
/**
* @brief Set pll freq.
* @param pSetup: Contains PLL register parameters
* @param rate: pll set
* @return HAL_Status.
*/
HAL_Status HAL_CRU_SetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate);
/**
* @brief Set pll power up.
* @param pSetup: Contains PLL register parameters
* @return HAL_Status.
*/
HAL_Status HAL_CRU_SetPllPowerUp(struct hwclk *hw, struct PLL_SETUP *pSetup);
/**
* @brief Set pll power down.
* @param pSetup: Contains PLL register parameters
* @return HAL_Status.
*/
HAL_Status HAL_CRU_SetPllPowerDown(struct hwclk *hw, struct PLL_SETUP *pSetup);
/**
* @brief Check if clk is enabled
* @param clk: clock to check
* @return HAL_Check.
*/
HAL_Check HAL_CRU_ClkIsEnabled(struct hwclk *hw, uint32_t clk);
/**
* @brief Enable clk
* @param clk: clock to set
* @return HAL_Status.
*/
HAL_Status HAL_CRU_ClkEnable(struct hwclk *hw, uint32_t clk);
/**
* @brief Disable clk
* @param clk: clock to set
* @return HAL_Status.
*/
HAL_Status HAL_CRU_ClkDisable(struct hwclk *hw, uint32_t clk);
/**
* @brief Check if clk is reset
* @param clk: clock to check
* @return HAL_Check.
*/
HAL_Check HAL_CRU_ClkIsReset(struct hwclk *hw, uint32_t clk);
/**
* @brief Assert the reset to the clk
* @param clk: clock to assert
* @return HAL_Status.
*/
HAL_Status HAL_CRU_ClkResetAssert(struct hwclk *hw, uint32_t clk);
/**
* @brief Deassert the reset to the clk
* @param clk: clock to deassert
* @return HAL_Status.
*/
HAL_Status HAL_CRU_ClkResetDeassert(struct hwclk *hw, uint32_t clk);
/**
* @brief Set integer div
* @param divName: div id(struct hwclk *hw, Contains div offset, shift, mask information)
* @param divValue: div value
* @return NONE
*/
HAL_Status HAL_CRU_ClkSetDiv(struct hwclk *hw, uint32_t divName, uint32_t divValue);
/**
* @brief Get integer div
* @param divName: div id (struct hwclk *hw,, Contains div offset, shift, mask information)
* @return div value
*/
uint32_t HAL_CRU_ClkGetDiv(struct hwclk *hw, uint32_t divName);
/**
* @brief Set mux
* @param muxName: mux id (struct hwclk *hw,, Contains mux offset, shift, mask information)
* @param muxValue: mux value
* @return NONE
*/
HAL_Status HAL_CRU_ClkSetMux(struct hwclk *hw, uint32_t muxName, uint32_t muxValue);
/**
* @brief Get mux
* @param muxName: mux id (struct hwclk *hw, Contains mux offset, shift, mask information)
* @return mux value
*/
uint32_t HAL_CRU_ClkGetMux(struct hwclk *hw, uint32_t muxName);
/**
* @brief Get clk freq.
* @param clockName: CLOCK_Name id.
* @return rate.
* @attention these APIs allow direct use in the HAL layer.
*/
uint32_t HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName);
/**
* @brief Set clk freq.
* @param clockName: CLOCK_Name id.
* @param rate: clk rate.
* @return HAL_Status.
* @attention these APIs allow direct use in the HAL layer.
*/
HAL_Status HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate);
/**
* @brief assert CRU global software reset.
* @param type: global software reset type.
* @return HAL_INVAL if the SoC does not support.
*/
HAL_Status HAL_CRU_SetGlbSrst(struct hwclk *hw, eCRU_GlbSrstType type);
/**
* @brief Set clk testout
* @param clockName: CLOCK_Name id.
* @param muxValue: mux value.
* @param divValue: div value.
* @return HAL_INVAL if the SoC does not support.
*/
HAL_Status HAL_CRU_ClkSetTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue);
/**
* @brief Dump all clock tree
*/
void HAL_CRU_ClkDumpTree(HAL_ClockType type);
/**
* @brief CRU init for chip
* @return HAL_INVAL if the SoC does not support.
*/
HAL_Status HAL_CRU_Init(void);
/**
* @brief Register CRU
* @param xfer: the data to register
* @return hwclk descriptor.
*/
struct hwclk *HAL_CRU_Register(struct xferclk xfer);
/**
* @brief Get hwclk
* @param client: the unit data to find hwclk
* @return hwclk descriptor.
*/
struct hwclk *HAL_CRU_ClkGet(void *client);
/************************** chip ops *************************************/
extern struct clkOps rkx110_clk_ops;
extern struct clkOps rkx120_clk_ops;
extern struct clkOps rkx111_clk_ops;
extern struct clkOps rkx121_clk_ops;
#endif

View File

@@ -0,0 +1,612 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#include "cru_core.h"
#include "cru_rkx110.h"
/*
* [RKX110 CHIP]: RX
*
* ================= SECTION: Input clock from devices =========================
*
* ### 300M ###
* clk_8x_pma2pcs0
* clk_8x_pma2pcs1
*
*
* ### 200M ###
* sclk_i2s_link2pcs --|-- sclk_i2s_link2pcs_inter1
* |-- sclk_i2s_link2pcs_inter2
*
*
* ### 200M ###
* clk_link_pcs0 --|-- clk_pma2pcs0 |-- clk_pma2link2pcs_link
* | |
* |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
* clk_link_pcs1 --| |
* |-- clk_pma2pcs1 |-- clk_pma2link2pcs_psc1
*
* clk_rxbytehs_csihost0
* clk_rxbytehs_csihost1
* iclk_dsi0
* iclk_dsi1
* iclk_vicap
*
*
* ### 150M ###
* dclk_lvds0 -- clk_d_lvds0_rklink_tx_cm --|-- clk_d_lvds0_pattern_gen_en
* |-- clk_d_lvds0_rklink_tx
*
* dclk_lvds1 -- clk_d_lvds1_rklink_tx_cm --|-- clk_d_lvds1_pattern_gen_en
* |-- clk_d_lvds1_rklink_tx
*
* clk_d_rgb_rklink_tx
* pclkin_vicap_dvp_rx
* rxpclk_lvds_align
* rxpclk_vicap_lvds
*/
#define RKX110_SSCG_RXPLL_EN 0
#define RKX110_SSCG_CPLL_EN 0
#define RKX110_TESTOUT_MUX -1 /* valid options: RKX110_TEST_CLKOUT_IOUT_SEL_? */
#define RKX110_GRF_GPIO0B_IOMUX_H 0x0001000c
#define GPIO0B7_TEST_CLKOUT 0x01c00080
#define I2S_122888_BEST_PRATE 393216000
#define I2S_112896_BEST_PRATE 756403200
static struct PLL_CONFIG PLL_TABLE[] = {
/* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
/* display */
RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
/* audio: 12.288M */
RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435), /* div=56, vco=2064.384M */
RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217), /* div=28, vco=1032.192M */
/* audio: 11.2896M */
RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340), /* div=42, vco=1896.6528M */
RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714), /* div=67, vco=1512.8064M */
RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088), /* div=50, vco=1128.96M */
{ /* sentinel */ },
};
static struct PLL_SETUP RXPLL_SETUP = {
.id = PLL_RXPLL,
.conOffset0 = 0x00000020,
.conOffset1 = 0x00000024,
.conOffset2 = 0x00000028,
.conOffset3 = 0x0000002c,
.modeOffset = 0x00000600,
.modeShift = 0, /* 0: slow-mode, 1: normal-mode */
.lockShift = 10,
.modeMask = 0x1,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX110_SSCG_RXPLL_EN,
};
static struct PLL_SETUP CPLL_SETUP = {
.id = PLL_CPLL,
.conOffset0 = 0x00000000,
.conOffset1 = 0x00000004,
.conOffset2 = 0x00000008,
.conOffset3 = 0x0000000c,
.modeOffset = 0x00000600,
.modeShift = 2,
.lockShift = 10,
.modeMask = 0x1 << 2,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX110_SSCG_CPLL_EN,
};
static uint32_t RKX11x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
{
uint32_t pRate = 0, freq = 0;
uint32_t clkMux, clkDiv;
if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN ||
clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
clockName = RKX110_CPS_DCLK_RX_PRE;
}
clkMux = CLK_GET_MUX(clockName);
clkDiv = CLK_GET_DIV(clockName);
switch (clockName) {
case RKX110_CPS_PLL_RXPLL:
hw->pllRate[RKX110_RXPLL] = HAL_CRU_GetPllFreq(hw, &RXPLL_SETUP);
return hw->pllRate[RKX110_RXPLL];
case RKX110_CPS_PLL_CPLL:
hw->pllRate[RKX110_CPLL] = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
return hw->pllRate[RKX110_CPLL];
/*
* 400MHZ => down to 200M
* === RX_DCLK_RX_PRE gate children ===
*
* dclk_dsi0
* dclk_dsi1
* dclk_vicap_csi
*
* clk_c_dvp_rklink_tx
* clk_c_csi_rklink_tx
*
* clk_d_dsi_0_rklink_tx
* clk_d_dsi_1_rklink_tx
* clk_d_dsi_0_pattern_gen
* clk_d_dsi_1_pattern_gen
* clk_c_lvds_rklink_tx
*
* NOTE: `clk_d_rgb_rklink_tx` is an input clock.
*
*/
case RKX110_CPS_DCLK_RX_PRE:
/* camera: 150M */
case RKX110_CPS_CLK_CAM0_OUT2IO:
case RKX110_CPS_CLK_CAM1_OUT2IO:
case RKX110_CPS_CLK_CIF_OUT2IO:
/* dsi: 200M */
case RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:
case RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:
/* lvds: 300M */
case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
/* i2s: 600M => down to 300M */
case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
freq = HAL_CRU_MuxGetFreq3(hw, clkMux, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], OSC_24M);
break;
/* mipi: ref_1000M, cfg_100M */
case RKX110_CPS_CKREF_MIPIRXPHY0:
case RKX110_CPS_CKREF_MIPIRXPHY1:
case RKX110_CPS_CFGCLK_MIPIRXPHY0:
case RKX110_CPS_CFGCLK_MIPIRXPHY1:
/* pre-bus: 100M */
case RKX110_CPS_BUSCLK_RX_PRE0:
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL]);
break;
/*
* bus: 100MHZ => down to 24M
*
* === RX_BUSCLK_RX_PRE gate children ===
*
* pclk_rx_cru
* pclk_rx_grf
* pclk_rx_gpio0/1
* pclk_rx_efuse
* pclk_mipi_grf_rx0/1
* pclk_rx_i2c2apb
* pclk_rx_i2c2apb_debug
* pclk_csihost0/1
* pclk_rklink_tx
* pclk_dsi_{0,1}_pattern_gen
* pclk_lvds_{0,1}_pattern_gen
* pclk_pcs0/1
* pclk_pcs{0,1}_ada
* pclk_mipirxphy0/1
* pclk_dft2apb
*
* hclk_vicap
* hclk_dsi0/1
*/
case RKX110_CPS_BUSCLK_RX_PRE:
pRate = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
break;
case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
return _MHZ(200);
/* gpio: 24M */
case RKX110_CPS_DCLK_RX_GPIO0:
case RKX110_CPS_DCLK_RX_GPIO1:
/* efuse: 24M */
case RKX110_CPS_RX_EFUSE:
/* pcs_ada: 24M */
case RKX110_CPS_PCS0_ADA:
case RKX110_CPS_PCS1_ADA:
return OSC_24M;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return 0;
}
if (clkDiv) {
freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
}
return freq;
}
static HAL_Status RKX11x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
{
uint32_t clkMux, clkDiv;
uint32_t mux = 0, div = 1;
uint32_t pRate = 0;
uint32_t maxDiv;
uint32_t pll;
uint8_t overMax = 0;
HAL_Status ret = HAL_OK;
if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN ||
clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
clockName = RKX110_CPS_DCLK_RX_PRE;
}
clkMux = CLK_GET_MUX(clockName);
clkDiv = CLK_GET_DIV(clockName);
switch (clockName) {
case RKX110_CPS_PLL_RXPLL:
ret = HAL_CRU_SetPllFreq(hw, &RXPLL_SETUP, rate);
if (ret != HAL_OK) {
CRU_ERR("%s: RXPLL set rate: %d failed\n", hw->name, rate);
} else {
hw->pllRate[RKX110_RXPLL] = rate;
CRU_MSG("%s: RXPLL set rate: %d\n", hw->name, rate);
}
return ret;
case RKX110_CPS_PLL_CPLL:
ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
if (ret != HAL_OK) {
CRU_ERR("%s: CPLL set rate: %d failed\n", hw->name, rate);
} else {
hw->pllRate[RKX110_CPLL] = rate;
CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
}
return ret;
/* link(dclk): Allowed to change PLL rate if need ! */
case RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:
case RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:
case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
/* i2s */
case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
if (DIV_NO_REM(hw->pllRate[RKX110_RXPLL], rate, maxDiv)) {
mux = 0;
pRate = hw->pllRate[RKX110_RXPLL];
} else if (DIV_NO_REM(hw->pllRate[RKX110_CPLL], rate, maxDiv)) {
mux = 1;
pRate = hw->pllRate[RKX110_CPLL];
} else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
mux = 2;
pRate = OSC_24M;
} else {
if (clockName == RKX110_CPS_CLK_I2S_SRC_RKLINK_TX) {
pll = RKX110_CPS_PLL_CPLL;
if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_122888_BEST_PRATE;
} else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_112896_BEST_PRATE;
}
} else {
pll = RKX110_CPS_PLL_RXPLL;
}
/* PLL change closest new rate <= 1200M if need */
if (!pRate) {
pRate = (_MHZ(1200) / rate) * rate;
}
ret = RKX11x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
if (ret != HAL_OK) {
return ret;
}
/* if success, continue to set divider */
}
break;
/* bus */
case RKX110_CPS_DCLK_RX_PRE:
/* camera */
case RKX110_CPS_CLK_CAM0_OUT2IO:
case RKX110_CPS_CLK_CAM1_OUT2IO:
case RKX110_CPS_CLK_CIF_OUT2IO:
mux = HAL_CRU_RoundFreqGetMux3(hw, rate, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], OSC_24M, &pRate);
break;
/* mipi */
case RKX110_CPS_CKREF_MIPIRXPHY0:
case RKX110_CPS_CKREF_MIPIRXPHY1:
case RKX110_CPS_CFGCLK_MIPIRXPHY0:
case RKX110_CPS_CFGCLK_MIPIRXPHY1:
/* pre-bus */
case RKX110_CPS_BUSCLK_RX_PRE0:
mux = HAL_CRU_RoundFreqGetMux2(hw, rate, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], &pRate);
break;
/* bus */
case RKX110_CPS_BUSCLK_RX_PRE:
if (rate == OSC_24M) {
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* gpio */
case RKX110_CPS_DCLK_RX_GPIO0:
case RKX110_CPS_DCLK_RX_GPIO1:
/* efuse */
case RKX110_CPS_RX_EFUSE:
/* pcs_ada */
case RKX110_CPS_PCS0_ADA:
case RKX110_CPS_PCS1_ADA:
return rate == OSC_24M ? 0 : HAL_INVAL;
case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
if (rate != _MHZ(200)) {
return HAL_INVAL;
}
HAL_CRU_ClkSetMux(hw, clkMux, 0);
return HAL_OK;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return HAL_INVAL;
}
if (pRate) {
div = HAL_DIV_ROUND_UP(pRate, rate);
}
if (clkDiv) {
overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
if (overMax) {
CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
__func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
div = CLK_DIV_GET_MAXDIV(clkDiv);
}
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
}
if (clkMux) {
HAL_CRU_ClkSetMux(hw, clkMux, mux);
}
return HAL_OK;
}
#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
static void RKX11x_HAL_CRU_Init_SSCG(struct hwclk *hw)
{
uint8_t down_spread = 1; /* 0: center spread */
uint8_t amplitude = 8; /* range: 0x00 - 0x1f */
#if RKX110_SSCG_CPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
#endif
#if RKX110_SSCG_RXPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
CRU_MSG("%s: RXPLL enable SSCG\n", hw->name);
#endif
}
#endif
static HAL_Status RKX11x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
/* gpio0_b7: iomux to clk_testout */
HAL_CRU_Write(hw, RKX110_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
/* Enable clock */
HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
/* Mux, div */
HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
return HAL_OK;
}
static HAL_Status RKX11x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
{
hw->cru_base = 0x0;
hw->sel_con0 = hw->cru_base + 0x100;
hw->gate_con0 = hw->cru_base + 0x300;
hw->softrst_con0 = hw->cru_base + 0x400;
hw->gbl_srst_fst = 0x0614;
hw->flags = 0;
hw->num_gate = 16 * 12;
hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
if (!hw->gate) {
return HAL_NOMEM;
}
strcat(hw->name, "<CRU.110@");
strcat(hw->name, xfer->name);
strcat(hw->name, ">");
/* Don't change order */
#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
RKX11x_HAL_CRU_Init_SSCG(hw);
#endif
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_CPLL, _MHZ(1200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_RXPLL, _MHZ(1188));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE, _MHZ(24));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_PMA2LINK2PCS_CM, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_DCLK_RX_PRE, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, _MHZ(300));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CFGCLK_MIPIRXPHY0, _MHZ(100));
HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
#if RKX110_TESTOUT_MUX >= 0
/* clk_testout support max 150M output, so set div=10 by default if not 24M */
RKX11x_HAL_CRU_InitTestout(hw, RKX110_CPS_TEST_CLKOUT, RKX110_TESTOUT_MUX,
RKX110_TESTOUT_MUX == 0 ? 1 : 10);
#endif
return HAL_OK;
}
PNAME(mux_24m_p) = { "xin24m" };
PNAME(mux_rxpll_cpll_p) = { "rxpll", "cpll" };
PNAME(mux_rxpll_cpll_24m_p) = { "rxpll", "cpll", "xin24m" };
PNAME(mux_rxpre0_24m_p) = { "xin24m", "busclk_rx_pre0" };
#define CAL_FREQ_REG 0xf00
static uint32_t RKX11x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
{
uint32_t clkMux = CLK_GET_MUX(RKX110_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX110_CPS_TEST_CLKOUT);
uint32_t freq, mhz;
uint8_t div = 10;
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
HAL_CRU_ClkSetMux(hw, clkMux, 0);
HAL_SleepMs(2);
HAL_CRU_ClkSetMux(hw, clkMux, clk);
HAL_SleepMs(2);
freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
/* Fix accuracy */
if ((freq % 10) == 0x9) {
freq++;
}
freq *= (1000 * div);
/* If no external input, freq is close to 24M */
mhz = freq / 1000000;
if ((clk != 0) && (mhz == 23 || mhz == 24)) {
freq = 0;
}
return freq;
}
static struct clkTable rkx11x_clkTable[] = {
/* internal */
CLK_DECLARE_INT("rxpll", RKX110_CPS_PLL_RXPLL, mux_24m_p),
CLK_DECLARE_INT("cpll", RKX110_CPS_PLL_CPLL, mux_24m_p),
CLK_DECLARE_INT("dclk_rx_pre", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_dsi_0_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_dsi_1_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cam0_out2io", RKX110_CPS_CLK_CAM0_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cam1_out2io", RKX110_CPS_CLK_CAM1_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cif_out2io", RKX110_CPS_CLK_CIF_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("dclk_d_dsi_0_rec_rklink_tx", RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("dclk_d_dsi_1_rec_rklink_tx", RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_2x_lvds_rklink_tx", RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_i2s_src_rklink_tx", RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("ckref_mipirxphy0", RKX110_CPS_CKREF_MIPIRXPHY0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("ckref_mipirxphy1", RKX110_CPS_CKREF_MIPIRXPHY1, mux_rxpll_cpll_p),
CLK_DECLARE_INT("cfgclk_mipirxphy0", RKX110_CPS_CFGCLK_MIPIRXPHY0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("cfgclk_mipirxphy1", RKX110_CPS_CFGCLK_MIPIRXPHY1, mux_rxpll_cpll_p),
CLK_DECLARE_INT("busclk_rx_pre0", RKX110_CPS_BUSCLK_RX_PRE0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("busclk_rx_pre", RKX110_CPS_BUSCLK_RX_PRE, mux_rxpre0_24m_p),
CLK_DECLARE_INT("dclk_rx_gpio0", RKX110_CPS_DCLK_RX_GPIO0, mux_24m_p),
CLK_DECLARE_INT("dclk_rx_gpio1", RKX110_CPS_DCLK_RX_GPIO1, mux_24m_p),
CLK_DECLARE_INT("rx_efuse", RKX110_CPS_RX_EFUSE, mux_24m_p),
CLK_DECLARE_INT("pcs0_ada", RKX110_CPS_PCS0_ADA, mux_24m_p),
CLK_DECLARE_INT("pcs1_ada", RKX110_CPS_PCS1_ADA, mux_24m_p),
/* external */
CLK_DECLARE_EXT("xin24m", 0, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("rxpclk_vicap_lvds", 3, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxbytehs_csihost0", 4, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxbytehs_csihost1", 5, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_d_rgb_rklink_tx", 6, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_lvds0", 7, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_lvds1", 8, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("pclkin_vicap_dvp_rx", 21, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("iclk_dsi0", 22, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("iclk_dsi1", 23, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("rxpclk_lvds_align", 24, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_8x_pma2pcs0", 25, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_8x_pma2pcs1", 26, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx_cm", 7, "dclk_lvds0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_pattern_gen_en", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx_cm", 8, "dclk_lvds1", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_pattern_gen_en", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 9, "clk_link_pcs1", RKX11x_HAL_CRU_ClkGetExtFreq),
{ /* sentinel */ },
};
struct clkOps rkx110_clk_ops =
{
.clkTable = rkx11x_clkTable,
.clkInit = RKX11x_HAL_CRU_Init,
.clkGetFreq = RKX11x_HAL_CRU_ClkGetFreq,
.clkSetFreq = RKX11x_HAL_CRU_ClkSetFreq,
.clkInitTestout = RKX11x_HAL_CRU_InitTestout,
};

View File

@@ -0,0 +1,358 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_RKX110_H
#include "cru_core.h"
// ======================== RXCRU module definition START ======================
// RXCRU_SOFTRST_CON01(Offset:0x404)
#define RKX110_SRST_PRESETN_RX_CRU 0x00000010
#define RKX110_SRST_PRESETN_RX_GRF 0x00000011
#define RKX110_SRST_PRESETN_RX_GPIO0 0x00000012
#define RKX110_SRST_DRESETN_RX_GPIO0 0x00000013
#define RKX110_SRST_PRESETN_RX_GPIO1 0x00000014
#define RKX110_SRST_DRESETN_RX_GPIO1 0x00000015
#define RKX110_SRST_PRESETN_RX_EFUSE 0x00000016
#define RKX110_SRST_RESETN_RX_EFUSE 0x00000017
#define RKX110_SRST_PRESETN_MIPI_GRF_RX0 0x0000001A
#define RKX110_SRST_PRESETN_MIPI_GRF_RX1 0x0000001B
#define RKX110_SRST_PRESETN_RX_I2C2APB 0x0000001E
#define RKX110_SRST_PRESETN_RX_I2C2APB_DEBUG 0x0000001F
// RXCRU_SOFTRST_CON02(Offset:0x408)
#define RKX110_SRST_DRESETN_VICAP_CSI 0x00000021
#define RKX110_SRST_HRESETN_VICAP 0x00000022
#define RKX110_SRST_IRESETN_VICAP 0x00000023
#define RKX110_SRST_RXPRESETN_VICAP_LVDS 0x00000024
#define RKX110_SRST_PRESETN_VICAP_DVP_RX 0x00000025
#define RKX110_SRST_DRESETN_DSI0 0x00000026
#define RKX110_SRST_HRESETN_DSI0 0x00000027
#define RKX110_SRST_IRESETN_DSI0 0x00000028
#define RKX110_SRST_RXPRESETN_LVDS_ALIGN 0x00000029
#define RKX110_SRST_DRESETN_DSI1 0x0000002A
#define RKX110_SRST_HRESETN_DSI1 0x0000002B
#define RKX110_SRST_IRESETN_DSI1 0x0000002C
// RXCRU_SOFTRST_CON03(Offset:0x40C)
#define RKX110_SRST_PRESETN_CSIHOST0 0x00000030
#define RKX110_SRST_PRESETN_CSIHOST1 0x00000032
// RXCRU_SOFTRST_CON04(Offset:0x410)
#define RKX110_SRST_PRESETN_RKLINK_TX 0x00000040
#define RKX110_SRST_RESETN_C_DVP_RKLINK_TX 0x00000041
#define RKX110_SRST_RESETN_C_CSI_RKLINK_TX 0x00000042
#define RKX110_SRST_RESETN_C_LVDS_RKLINK_TX 0x00000043
#define RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX 0x00000044
#define RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX 0x00000045
#define RKX110_SRST_RESETN_D_RGB_RKLINK_TX 0x00000046
#define RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX 0x00000048
#define RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX 0x0000004A
#define RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX 0x0000004B
#define RKX110_SRST_RESETN_I2S_SRC_RKLINK_TX 0x0000004C
#define RKX110_SRST_RESETN_D_DSI_0_REC_RKLINK_TX 0x0000004D
#define RKX110_SRST_RESETN_D_DSI_1_REC_RKLINK_TX 0x0000004E
// RXCRU_SOFTRST_CON05(Offset:0x414)
#define RKX110_SRST_RESETN_PMA2LINK2PCS_LINK 0x00000051
#define RKX110_SRST_RESETN_PMA2LINK2PCS_PCS0 0x00000052
#define RKX110_SRST_RESETN_PMA2LINK2PCS_PCS1 0x00000053
#define RKX110_SRST_SRESETN_I2S_PCS0 0x00000054
#define RKX110_SRST_SRESETN_I2S_PCS1 0x00000055
// RXCRU_SOFTRST_CON06(Offset:0x418)
#define RKX110_SRST_RESETN_D_DSI_0_PATTERN_GEN 0x00000060
#define RKX110_SRST_RESETN_D_DSI_1_PATTERN_GEN 0x00000061
#define RKX110_SRST_RESETN_D_LVDS0_PATTERN_GEN 0x00000062
#define RKX110_SRST_RESETN_D_LVDS1_PATTERN_GEN 0x00000063
#define RKX110_SRST_PRESETN_DSI_0_PATTERN_GEN 0x00000068
#define RKX110_SRST_PRESETN_DSI_1_PATTERN_GEN 0x00000069
#define RKX110_SRST_PRESETN_LVDS_0_PATTERN_GEN 0x0000006A
#define RKX110_SRST_PRESETN_LVDS_1_PATTERN_GEN 0x0000006B
// RXCRU_SOFTRST_CON07(Offset:0x41C)
#define RKX110_SRST_PRESETN_PCS0 0x00000070
#define RKX110_SRST_RESETN_8X_PMA2PCS0 0x00000071
#define RKX110_SRST_RESETN_PMA2PCS0 0x00000073
#define RKX110_SRST_PRESETN_PCS0_ADA 0x00000074
#define RKX110_SRST_RESETN_PCS0_ADA 0x00000075
// RXCRU_SOFTRST_CON08(Offset:0x420)
#define RKX110_SRST_PRESETN_PCS1 0x00000080
#define RKX110_SRST_RESETN_8X_PMA2PCS1 0x00000081
#define RKX110_SRST_RESETN_PMA2PCS1 0x00000083
#define RKX110_SRST_PRESETN_PCS1_ADA 0x00000084
#define RKX110_SRST_RESETN_PCS1_ADA 0x00000085
// RXCRU_SOFTRST_CON10(Offset:0x428)
#define RKX110_SRST_PRESETN_MIPIRXPHY0 0x000000A0
#define RKX110_SRST_CFGRESETN_MIPIRXPHY0 0x000000A1
#define RKX110_SRST_PRESETN_MIPIRXPHY1 0x000000A8
#define RKX110_SRST_CFGRESETN_MIPIRXPHY1 0x000000A9
// RXCRU_SOFTRST_CON11(Offset:0x42C)
#define RKX110_SRST_PRESETN_DFT2APB 0x000000B0
// RXCRU_GATE_CON00(Offset:0x300)
#define RKX110_CLK_TESTOUT_TOP_GATE 0x00000000
#define RKX110_BUSCLK_RX_PRE0_GATE 0x00000001
#define RKX110_BUSCLK_RX_PRE_GATE 0x00000002
// RXCRU_GATE_CON01(Offset:0x304)
#define RKX110_PCLK_RX_CRU_GATE 0x00000010
#define RKX110_PCLK_RX_GRF_GATE 0x00000011
#define RKX110_PCLK_RX_GPIO0_GATE 0x00000012
#define RKX110_DCLK_RX_GPIO0_GATE 0x00000013
#define RKX110_PCLK_RX_GPIO1_GATE 0x00000014
#define RKX110_DCLK_RX_GPIO1_GATE 0x00000015
#define RKX110_PCLK_RX_EFUSE_GATE 0x00000016
#define RKX110_CLK_RX_EFUSE_GATE 0x00000017
#define RKX110_PCLK_MIPI_GRF_RX0_GATE 0x0000001A
#define RKX110_PCLK_MIPI_GRF_RX1_GATE 0x0000001B
#define RKX110_PCLK_RX_I2C2APB_GATE 0x0000001E
#define RKX110_PCLK_RX_I2C2APB_DEBUG_GATE 0x0000001F
// RXCRU_GATE_CON02(Offset:0x308)
#define RKX110_DCLK_RX_PRE_GATE 0x00000020
#define RKX110_DCLK_VICAP_CSI_GATE 0x00000021
#define RKX110_HCLK_VICAP_GATE 0x00000022
#define RKX110_ICLK_VICAP_INTER_GATE 0x00000023
#define RKX110_RXPCLK_VICAP_LVDS_DFT_GATE 0x00000024
#define RKX110_PCLKIN_VICAP_DVP_RX_DFT_GATE 0x00000025
#define RKX110_DCLK_DSI0_GATE 0x00000026
#define RKX110_HCLK_DSI0_GATE 0x00000027
#define RKX110_ICLK_DSI0_INTER_GATE 0x00000028
#define RKX110_RXPCLK_LVDS_ALIGN_DFT_GATE 0x00000029
#define RKX110_DCLK_DSI1_GATE 0x0000002A
#define RKX110_HCLK_DSI1_GATE 0x0000002B
#define RKX110_ICLK_DSI1_INTER_GATE 0x0000002C
// RXCRU_GATE_CON03(Offset:0x30C)
#define RKX110_PCLK_CSIHOST0_GATE 0x00000030
#define RKX110_CLK_RXBYTEHS_CSIHOST0_DFT_GATE 0x00000031
#define RKX110_PCLK_CSIHOST1_GATE 0x00000032
#define RKX110_CLK_RXBYTEHS_CSIHOST1_DFT_GATE 0x00000033
// RXCRU_GATE_CON04(Offset:0x310)
#define RKX110_PCLK_RKLINK_TX_GATE 0x00000040
#define RKX110_CLK_C_DVP_RKLINK_TX_GATE 0x00000041
#define RKX110_CLK_C_CSI_RKLINK_TX_GATE 0x00000042
#define RKX110_CLK_C_LVDS_RKLINK_TX_GATE 0x00000043
#define RKX110_CLK_D_DSI_0_RKLINK_TX_GATE 0x00000044
#define RKX110_CLK_D_DSI_1_RKLINK_TX_GATE 0x00000045
#define RKX110_CLK_D_RGB_RKLINK_TX_DFT_GATE 0x00000046
#define RKX110_CLK_D_LVDS0_RKLINK_TX_CM_GATE 0x00000047
#define RKX110_CLK_D_LVDS0_RKLINK_TX_GATE 0x00000048
#define RKX110_CLK_D_LVDS1_RKLINK_TX_CM_GATE 0x00000049
#define RKX110_CLK_D_LVDS1_RKLINK_TX_GATE 0x0000004A
#define RKX110_CLK_2X_LVDS_RKLINK_TX_GATE 0x0000004B
#define RKX110_CLK_I2S_SRC_RKLINK_TX_GATE 0x0000004C
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_GATE 0x0000004D
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_GATE 0x0000004E
// RXCRU_GATE_CON05(Offset:0x314)
#define RKX110_CLK_PMA2LINK2PCS_CM_GATE 0x00000050
#define RKX110_CLK_PMA2LINK2PCS_LINK_GATE 0x00000051
#define RKX110_CLK_PMA2LINK2PCS_PSC0_GATE 0x00000052
#define RKX110_CLK_PMA2LINK2PCS_PSC1_GATE 0x00000053
#define RKX110_SCLK_I2S_LINK2PCS_INTER1_GATE 0x00000054
#define RKX110_SCLK_I2S_LINK2PCS_INTER2_GATE 0x00000055
// RXCRU_GATE_CON06(Offset:0x318)
#define RKX110_CLK_D_DSI_0_PATTERN_GEN_GATE 0x00000060
#define RKX110_CLK_D_DSI_1_PATTERN_GEN_GATE 0x00000061
#define RKX110_CLK_D_LVDS0_PATTERN_GEN_GATE 0x00000062
#define RKX110_CLK_D_LVDS1_PATTERN_GEN_GATE 0x00000063
#define RKX110_PCLK_DSI_0_PATTERN_GEN_GATE 0x00000068
#define RKX110_PCLK_DSI_1_PATTERN_GEN_GATE 0x00000069
#define RKX110_PCLK_LVDS_0_PATTERN_GEN_GATE 0x0000006A
#define RKX110_PCLK_LVDS_1_PATTERN_GEN_GATE 0x0000006B
// RXCRU_GATE_CON07(Offset:0x31C)
#define RKX110_PCLK_PCS0_GATE 0x00000070
#define RKX110_CLK_8X_PMA2PCS0_DFT_GATE 0x00000071
#define RKX110_CLK_LINK_PCS0_DFT_GATE 0x00000072
#define RKX110_CLK_PMA2PCS0_GATE 0x00000073
#define RKX110_PCLK_PCS0_ADA_GATE 0x00000074
#define RKX110_CLK_PCS0_ADA_GATE 0x00000075
// RXCRU_GATE_CON08(Offset:0x320)
#define RKX110_PCLK_PCS1_GATE 0x00000080
#define RKX110_CLK_8X_PMA2PCS1_DFT_GATE 0x00000081
#define RKX110_CLK_LINK_PCS1_DFT_GATE 0x00000082
#define RKX110_CLK_PMA2PCS1_GATE 0x00000083
#define RKX110_PCLK_PCS1_ADA_GATE 0x00000084
#define RKX110_CLK_PCS1_ADA_GATE 0x00000085
// RXCRU_GATE_CON09(Offset:0x324)
#define RKX110_CLK_CAM_OUT2IO_GATE 0x00000090
// RXCRU_GATE_CON10(Offset:0x328)
#define RKX110_PCLK_MIPIRXPHY0_GATE 0x000000A0
#define RKX110_CFGCLK_MIPIRXPHY0_GATE 0x000000A1
#define RKX110_CKREF_MIPIRXPHY0_GATE 0x000000A2
#define RKX110_PCLK_MIPIRXPHY1_GATE 0x000000A8
#define RKX110_CFGCLK_MIPIRXPHY1_GATE 0x000000A9
#define RKX110_CKREF_MIPIRXPHY1_GATE 0x000000AA
#define RKX110_CLK_CAM0_OUT2IO_GATE 0x000000AB
#define RKX110_CLK_CAM1_OUT2IO_GATE 0x000000AC
// RXCRU_GATE_CON11(Offset:0x32C)
#define RKX110_PCLK_DFT2APB_GATE 0x000000B0
// RXCRU_CLKSEL_CON00(Offset:0x100)
#define RKX110_TEST_CLKOUT_IOUT_DIV 0x08000000
#define RKX110_TEST_CLKOUT_IOUT_SEL 0x05080000
#define RKX110_TEST_CLKOUT_IOUT_SEL_XIN_OSC0_FUNC 0U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXPLL_MUX 1U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_CPLL_MUX 2U
#define RKX110_TEST_CLKOUT_IOUT_SEL_RXPCLK_VICAP_LVDS 3U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXBYTEHS_CSIHOST0 4U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXBYTEHS_CSIHOST1 5U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_D_RGB_RKLINK_TX 6U
#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_LVDS0 7U
#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_LVDS1 8U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS0 9U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS1 10U
#define RKX110_TEST_CLKOUT_IOUT_SEL_BUSCLK_RX_PRE 11U
#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_RX_PRE 12U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_2X_LVDS_RKLINK_TX 13U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_I2S_SRC_RKLINK_TX 14U
#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_0_REC_RKLINK_TX 15U
#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_1_REC_RKLINK_TX 16U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CFGCLK_MIPIRXPHY0 17U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CKREF_MIPIRXPHY0 18U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CFGCLK_MIPIRXPHY1 19U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CKREF_MIPIRXPHY1 20U
#define RKX110_TEST_CLKOUT_IOUT_SEL_PCLKIN_VICAP_DVP_RX 21U
#define RKX110_TEST_CLKOUT_IOUT_SEL_ICLK_DSI0 22U
#define RKX110_TEST_CLKOUT_IOUT_SEL_ICLK_DSI1 23U
#define RKX110_TEST_CLKOUT_IOUT_SEL_RXPCLK_LVDS_ALIGN 24U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_8X_PMA2PCS0 25U
#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_8X_PMA2PCS1 26U
// RXCRU_CLKSEL_CON01(Offset:0x104)
#define RKX110_BUSCLK_RX_PRE0_DIV 0x06000001
#define RKX110_BUSCLK_RX_PRE0_SEL 0x01070001
#define RKX110_BUSCLK_RX_PRE0_SEL_CLK_RXPLL_MUX 0U
#define RKX110_BUSCLK_RX_PRE0_SEL_CLK_CPLL_MUX 1U
#define RKX110_BUSCLK_RX_PRE_SEL 0x01080001
#define RKX110_BUSCLK_RX_PRE_SEL_XIN_OSC0_FUNC 0U
#define RKX110_BUSCLK_RX_PRE_SEL_BUSCLK_RX_PRE0 1U
// RXCRU_CLKSEL_CON02(Offset:0x108)
#define RKX110_DCLK_RX_PRE_DIV 0x06000002
#define RKX110_DCLK_RX_PRE_SEL 0x02060002
#define RKX110_DCLK_RX_PRE_SEL_CLK_RXPLL_MUX 0U
#define RKX110_DCLK_RX_PRE_SEL_CLK_CPLL_MUX 1U
#define RKX110_DCLK_RX_PRE_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON03(Offset:0x10C)
#define RKX110_CLK_2X_LVDS_RKLINK_TX_DIV 0x08000003
#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL 0x020E0003
#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON04(Offset:0x110)
#define RKX110_CLK_I2S_SRC_RKLINK_TX_DIV 0x08000004
#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL 0x020E0004
#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON05(Offset:0x114)
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_DIV 0x08000005
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL 0x020E0005
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON06(Offset:0x118)
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_DIV 0x08000006
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL 0x020E0006
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON07(Offset:0x11C)
#define RKX110_CLK_PMA2LINK2PCS_CM_SEL 0x010F0007
#define RKX110_CLK_PMA2LINK2PCS_CM_SEL_CLK_LINK_PCS0_DFT 0U
#define RKX110_CLK_PMA2LINK2PCS_CM_SEL_CLK_LINK_PCS1_DFT 1U
// RXCRU_CLKSEL_CON08(Offset:0x120)
#define RKX110_CLK_CIF_OUT2IO_DIV 0x08000008
#define RKX110_CLK_CIF_OUT2IO_SEL 0x020E0008
#define RKX110_CLK_CIF_OUT2IO_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CLK_CIF_OUT2IO_SEL_CLK_CPLL_MUX 1U
#define RKX110_CLK_CIF_OUT2IO_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON09(Offset:0x124)
#define RKX110_CFGCLK_MIPIRXPHY0_DIV 0x08000009
#define RKX110_CFGCLK_MIPIRXPHY0_SEL 0x010F0009
#define RKX110_CFGCLK_MIPIRXPHY0_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CFGCLK_MIPIRXPHY0_SEL_CLK_CPLL_MUX 1U
// RXCRU_CLKSEL_CON10(Offset:0x128)
#define RKX110_CKREF_MIPIRXPHY0_DIV 0x0400000A
#define RKX110_CKREF_MIPIRXPHY0_SEL 0x0107000A
#define RKX110_CKREF_MIPIRXPHY0_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CKREF_MIPIRXPHY0_SEL_CLK_CPLL_MUX 1U
// RXCRU_CLKSEL_CON11(Offset:0x12C)
#define RKX110_CFGCLK_MIPIRXPHY1_DIV 0x0800000B
#define RKX110_CFGCLK_MIPIRXPHY1_SEL 0x010F000B
#define RKX110_CFGCLK_MIPIRXPHY1_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CFGCLK_MIPIRXPHY1_SEL_CLK_CPLL_MUX 1U
// RXCRU_CLKSEL_CON12(Offset:0x130)
#define RKX110_CKREF_MIPIRXPHY1_DIV 0x0408000C
#define RKX110_CKREF_MIPIRXPHY1_SEL 0x010F000C
#define RKX110_CKREF_MIPIRXPHY1_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CKREF_MIPIRXPHY1_SEL_CLK_CPLL_MUX 1U
// RXCRU_CLKSEL_CON15(Offset:0x13C)
#define RKX110_CLK_CAM0_OUT2IO_DIV 0x0600000F
#define RKX110_CLK_CAM1_OUT2IO_DIV 0x0608000F
#define RKX110_CLK_CAM0_OUT2IO_SEL 0x0206000F
#define RKX110_CLK_CAM0_OUT2IO_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CLK_CAM0_OUT2IO_SEL_CLK_CPLL_MUX 1U
#define RKX110_CLK_CAM0_OUT2IO_SEL_XIN_OSC0_FUNC 2U
#define RKX110_CLK_CAM1_OUT2IO_SEL 0x020E000F
#define RKX110_CLK_CAM1_OUT2IO_SEL_CLK_RXPLL_MUX 0U
#define RKX110_CLK_CAM1_OUT2IO_SEL_CLK_CPLL_MUX 1U
#define RKX110_CLK_CAM1_OUT2IO_SEL_XIN_OSC0_FUNC 2U
// ======================== RXCRU module definition END ========================
#define RKX110_CPS_INVAL 0
#define RKX110_CPS_PLL_CPLL 1
#define RKX110_CPS_PLL_RXPLL 2
#define RKX110_CPS_DCLK_RX_GPIO0 3
#define RKX110_CPS_DCLK_RX_GPIO1 4
#define RKX110_CPS_RX_EFUSE 5
#define RKX110_CPS_PCS0_ADA 6
#define RKX110_CPS_PCS1_ADA 7
#define RKX110_CLK_D_DSI_0_PATTERN_GEN 8
#define RKX110_CLK_D_DSI_1_PATTERN_GEN 9
#define RKX110_CPS_DCLK_RX_PRE COMPOSITE_CLK(RKX110_DCLK_RX_PRE_SEL, RKX110_DCLK_RX_PRE_DIV)
#define RKX110_CPS_CLK_2X_LVDS_RKLINK_TX COMPOSITE_CLK(RKX110_CLK_2X_LVDS_RKLINK_TX_SEL, RKX110_CLK_2X_LVDS_RKLINK_TX_DIV)
#define RKX110_CPS_CLK_I2S_SRC_RKLINK_TX COMPOSITE_CLK(RKX110_CLK_I2S_SRC_RKLINK_TX_SEL, RKX110_CLK_I2S_SRC_RKLINK_TX_DIV)
#define RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX COMPOSITE_CLK(RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL, RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_DIV)
#define RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX COMPOSITE_CLK(RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL, RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_DIV)
#define RKX110_CPS_CLK_PMA2LINK2PCS_CM COMPOSITE_CLK(RKX110_CLK_PMA2LINK2PCS_CM_SEL, 0)
#define RKX110_CPS_CLK_CIF_OUT2IO COMPOSITE_CLK(RKX110_CLK_CIF_OUT2IO_SEL, RKX110_CLK_CIF_OUT2IO_DIV)
#define RKX110_CPS_CKREF_MIPIRXPHY0 COMPOSITE_CLK(RKX110_CKREF_MIPIRXPHY0_SEL, RKX110_CKREF_MIPIRXPHY0_DIV)
#define RKX110_CPS_CKREF_MIPIRXPHY1 COMPOSITE_CLK(RKX110_CKREF_MIPIRXPHY1_SEL, RKX110_CKREF_MIPIRXPHY1_DIV)
#define RKX110_CPS_CLK_CAM0_OUT2IO COMPOSITE_CLK(RKX110_CLK_CAM0_OUT2IO_SEL, RKX110_CLK_CAM0_OUT2IO_DIV)
#define RKX110_CPS_CLK_CAM1_OUT2IO COMPOSITE_CLK(RKX110_CLK_CAM1_OUT2IO_SEL, RKX110_CLK_CAM1_OUT2IO_DIV)
#define RKX110_CPS_BUSCLK_RX_PRE0 COMPOSITE_CLK(RKX110_BUSCLK_RX_PRE0_SEL, RKX110_BUSCLK_RX_PRE0_DIV)
#define RKX110_CPS_BUSCLK_RX_PRE COMPOSITE_CLK(RKX110_BUSCLK_RX_PRE_SEL, 0)
#define RKX110_CPS_CFGCLK_MIPIRXPHY0 COMPOSITE_CLK(RKX110_CFGCLK_MIPIRXPHY0_SEL, RKX110_CFGCLK_MIPIRXPHY0_DIV)
#define RKX110_CPS_CFGCLK_MIPIRXPHY1 COMPOSITE_CLK(RKX110_CFGCLK_MIPIRXPHY1_SEL, RKX110_CFGCLK_MIPIRXPHY1_DIV)
#define RKX110_CPS_TEST_CLKOUT COMPOSITE_CLK(RKX110_TEST_CLKOUT_IOUT_SEL, RKX110_TEST_CLKOUT_IOUT_DIV)
#endif

View File

@@ -0,0 +1,706 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#include "cru_core.h"
#include "cru_rkx111.h"
/*
* [RKX111 CHIP]: RX
*
* ================= SECTION: Input clock from devices =========================
*
* ### 400M ###
* clk_8x_pma2pcs0
* clk_8x_pma2pcs1
*
*
* ### 200M ###
* sclk_i2s_link2pcs --|-- sclk_i2s_link2pcs_inter1
* |-- sclk_i2s_link2pcs_inter2
*
*
* ### 150M ###
* clk_link_pcs0 --|-- clk_pma2pcs0 |-- clk_pma2link2pcs_link
* | |
* |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
* clk_link_pcs1 --| |
* |-- clk_pma2pcs1 |-- clk_pma2link2pcs_psc1
*
*
* clk_rxbytehs_csihost0
* clk_rxbytehs_csihost1
* iclk_dsi0
* iclk_dsi1
* iclk_vicap
*
*
* ### 200M ###
* dclk_lvds0 -- clk_d_lvds0_rklink_tx_cm --|-- clk_d_lvds0_pattern_gen_en
* |-- clk_d_lvds0_rklink_tx
*
* dclk_lvds1 -- clk_d_lvds1_rklink_tx_cm --|-- clk_d_lvds1_pattern_gen_en
* |-- clk_d_lvds1_rklink_tx
*
* clk_d_rgb_rklink_tx
* pclkin_vicap_dvp_rx
* rxpclk_lvds_align
* rxpclk_vicap_lvds
*/
#define RKX110_SSCG_RXPLL_EN 0
#define RKX110_SSCG_CPLL_EN 0
#define RKX110_TESTOUT_MUX -1 /* valid options: RKX110_TEST_CLKOUT_IOUT_SEL_? */
#define RKX110_GRF_GPIO0B_IOMUX_H 0x0001000c
#define GPIO0B7_TEST_CLKOUT 0x01c00080
#define I2S_122888_BEST_PRATE 393216000
#define I2S_112896_BEST_PRATE 756403200
static struct PLL_CONFIG PLL_TABLE[] = {
/* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
/* display */
RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
/* audio: 12.288M */
RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435), /* div=56, vco=2064.384M */
RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217), /* div=28, vco=1032.192M */
/* audio: 11.2896M */
RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340), /* div=42, vco=1896.6528M */
RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714), /* div=67, vco=1512.8064M */
RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088), /* div=50, vco=1128.96M */
{ /* sentinel */ },
};
static struct PLL_SETUP RXPLL_SETUP = {
.id = PLL_RXPLL,
.conOffset0 = 0x00000020,
.conOffset1 = 0x00000024,
.conOffset2 = 0x00000028,
.conOffset3 = 0x0000002c,
.modeOffset = 0x00000600,
.modeShift = 0, /* 0: slow-mode, 1: normal-mode */
.lockShift = 10,
.modeMask = 0x1,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX110_SSCG_RXPLL_EN,
};
static struct PLL_SETUP CPLL_SETUP = {
.id = PLL_CPLL,
.conOffset0 = 0x00000000,
.conOffset1 = 0x00000004,
.conOffset2 = 0x00000008,
.conOffset3 = 0x0000000c,
.modeOffset = 0x00000600,
.modeShift = 2,
.lockShift = 10,
.modeMask = 0x1 << 2,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX110_SSCG_CPLL_EN,
};
static uint32_t RKX11x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
{
uint32_t pRate = 0, freq = 0;
uint32_t pRate0 = 0, pRate1 = 0;
uint32_t clkMux, clkDiv;
if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN) {
clockName = RKX111_CPS_DCLK_D_DSI_0_REC;
}
if (clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
clockName = RKX111_CPS_DCLK_D_DSI_1_REC;
}
clkMux = CLK_GET_MUX(clockName);
clkDiv = CLK_GET_DIV(clockName);
switch (clockName) {
case RKX110_CPS_PLL_RXPLL:
hw->pllRate[RKX110_RXPLL] = HAL_CRU_GetPllFreq(hw, &RXPLL_SETUP);
return hw->pllRate[RKX110_RXPLL];
case RKX110_CPS_PLL_CPLL:
hw->pllRate[RKX110_CPLL] = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
return hw->pllRate[RKX110_CPLL];
/*
* 400MHZ => down to 200M
* === RX_DCLK_RX_PRE gate children ===
*
* dclk_dsi0
* dclk_vicap_csi
* clk_c_csi_rklink_tx
* clk_d_dsi_0_rklink_tx
*
* NOTE: `clk_d_rgb_rklink_tx` is an input clock.
*
*/
case RKX110_CPS_DCLK_RX_PRE:
/*
* === DCLK_RX_PRE_200M gate children ===
*
* clk_c_dvp_rklink_tx
* clk_c_lvds_rklink_tx
* clk_d_dsi_1_rklink_tx
* dclk_dsi1
* dclk_vicap_csi_ls
*/
case RKX111_CPS_DCLK_RX_PRE_200M:
/* camera: 150M */
case RKX110_CPS_CLK_CAM0_OUT2IO:
case RKX110_CPS_CLK_CAM1_OUT2IO:
case RKX110_CPS_CLK_CIF_OUT2IO:
/* dsi0: 200M, child: clk_d_dsi_0_pattern_gen */
case RKX111_CPS_DCLK_D_DSI_0_REC:
/* dsi1: 200M, child: clk_d_dsi_1_pattern_gen */
case RKX111_CPS_DCLK_D_DSI_1_REC:
/* lvds pattern gen: 150M */
case RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN:
case RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN:
/* lvds: 200M */
case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
/* i2s: 600M => down to 300M */
case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
freq = HAL_CRU_MuxGetFreq3(hw, clkMux, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], OSC_24M);
break;
/* mipi: ref_1000M, cfg_100M */
case RKX110_CPS_CKREF_MIPIRXPHY0:
case RKX110_CPS_CKREF_MIPIRXPHY1:
case RKX110_CPS_CFGCLK_MIPIRXPHY0:
case RKX110_CPS_CFGCLK_MIPIRXPHY1:
/* pre-bus: 100M */
case RKX110_CPS_BUSCLK_RX_PRE0:
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL]);
break;
/*
* bus: 100MHZ => down to 24M
*
* === RX_BUSCLK_RX_PRE gate children ===
*
* pclk_rx_cru
* pclk_rx_grf
* pclk_rx_gpio0/1
* pclk_rx_efuse
* pclk_mipi_grf_rx0/1
* pclk_rx_i2c2apb
* pclk_rx_i2c2apb_debug
* pclk_csihost0/1
* pclk_rklink_tx
* pclk_dsi_{0,1}_pattern_gen
* pclk_lvds_{0,1}_pattern_gen
* pclk_pcs0/1
* pclk_pcs{0,1}_ada
* pclk_mipirxphy0/1
* pclk_dft2apb
* pclk_lbist_ada_rx (new)
*
* hclk_vicap
* hclk_dsi0/1
*/
case RKX110_CPS_BUSCLK_RX_PRE:
pRate = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
break;
case RKX111_CPS_CLK_D_LVDS0_RKLINK_TX:
pRate0 = _MHZ(150); /* input clock: dclk_lvds0 */
pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
break;
case RKX111_CPS_CLK_D_LVDS1_RKLINK_TX:
pRate0 = _MHZ(150); /* input clock: dclk_lvds1 */
pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
break;
case RKX111_CPS_CLK_D_DSI_0_RKLINK_TX:
pRate0 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_DCLK_RX_PRE); /* 400M */
pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CLK_D_DSI_0_PATTERN_GEN);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
break;
case RKX111_CPS_CLK_D_DSI_1_RKLINK_TX:
pRate0 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_DCLK_RX_PRE_200M);
pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CLK_D_DSI_1_PATTERN_GEN);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
break;
case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
return _MHZ(200);
/* gpio: 24M */
case RKX110_CPS_DCLK_RX_GPIO0:
case RKX110_CPS_DCLK_RX_GPIO1:
/* efuse: 24M */
case RKX110_CPS_RX_EFUSE:
/* pcs_ada: 24M */
case RKX110_CPS_PCS0_ADA:
case RKX110_CPS_PCS1_ADA:
return OSC_24M;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return 0;
}
if (clkDiv) {
freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
}
return freq;
}
static HAL_Status RKX11x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
{
uint32_t clkMux, clkDiv;
uint32_t mux = 0, div = 1;
uint32_t pRate = 0;
uint32_t maxDiv;
uint32_t pll;
uint8_t overMax = 0;
HAL_Status ret = HAL_OK;
if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN) {
clockName = RKX111_CPS_DCLK_D_DSI_0_REC;
}
if (clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
clockName = RKX111_CPS_DCLK_D_DSI_1_REC;
}
clkMux = CLK_GET_MUX(clockName);
clkDiv = CLK_GET_DIV(clockName);
switch (clockName) {
case RKX110_CPS_PLL_RXPLL:
ret = HAL_CRU_SetPllFreq(hw, &RXPLL_SETUP, rate);
if (ret != HAL_OK) {
CRU_ERR("%s: RXPLL set rate: %d failed\n", hw->name, rate);
} else {
hw->pllRate[RKX110_RXPLL] = rate;
CRU_MSG("%s: RXPLL set rate: %d\n", hw->name, rate);
}
return ret;
case RKX110_CPS_PLL_CPLL:
ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
if (ret != HAL_OK) {
CRU_ERR("%s: CPLL set rate: %d failed\n", hw->name, rate);
} else {
hw->pllRate[RKX110_CPLL] = rate;
CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
}
return ret;
/* link(dclk): Allowed to change PLL rate if need ! */
case RKX111_CPS_DCLK_D_DSI_0_REC:
case RKX111_CPS_DCLK_D_DSI_1_REC:
case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
/* i2s */
case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
if (DIV_NO_REM(hw->pllRate[RKX110_RXPLL], rate, maxDiv)) {
mux = 0;
pRate = hw->pllRate[RKX110_RXPLL];
} else if (DIV_NO_REM(hw->pllRate[RKX110_CPLL], rate, maxDiv)) {
mux = 1;
pRate = hw->pllRate[RKX110_CPLL];
} else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
mux = 2;
pRate = OSC_24M;
} else {
if (clockName == RKX110_CPS_CLK_I2S_SRC_RKLINK_TX) {
pll = RKX110_CPS_PLL_CPLL;
if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_122888_BEST_PRATE;
} else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_112896_BEST_PRATE;
}
} else {
pll = RKX110_CPS_PLL_RXPLL;
}
/* PLL change closest new rate <= 1200M if need */
if (!pRate) {
pRate = (_MHZ(1200) / rate) * rate;
}
ret = RKX11x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
if (ret != HAL_OK) {
return ret;
}
/* if success, continue to set divider */
}
break;
/* bus */
case RKX110_CPS_DCLK_RX_PRE:
case RKX111_CPS_DCLK_RX_PRE_200M:
/* lvds */
case RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN:
case RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN:
/* camera */
case RKX110_CPS_CLK_CAM0_OUT2IO:
case RKX110_CPS_CLK_CAM1_OUT2IO:
case RKX110_CPS_CLK_CIF_OUT2IO:
mux = HAL_CRU_RoundFreqGetMux3(hw, rate, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], OSC_24M, &pRate);
break;
/* mipi */
case RKX110_CPS_CKREF_MIPIRXPHY0:
case RKX110_CPS_CKREF_MIPIRXPHY1:
case RKX110_CPS_CFGCLK_MIPIRXPHY0:
case RKX110_CPS_CFGCLK_MIPIRXPHY1:
/* pre-bus */
case RKX110_CPS_BUSCLK_RX_PRE0:
mux = HAL_CRU_RoundFreqGetMux2(hw, rate, hw->pllRate[RKX110_RXPLL],
hw->pllRate[RKX110_CPLL], &pRate);
break;
/* lvds0/1 rklink */
case RKX111_CPS_CLK_D_LVDS0_RKLINK_TX:
if (rate == _MHZ(150)) { /* input clock: dclk_lvds0/1 */
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
case RKX111_CPS_CLK_D_LVDS1_RKLINK_TX:
if (rate == _MHZ(150)) { /* input clock: dclk_lvds0/1 */
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
case RKX111_CPS_CLK_D_DSI_0_RKLINK_TX:
if (rate == _MHZ(400)) { /* RKX110_CPS_DCLK_RX_PRE */
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
case RKX111_CPS_CLK_D_DSI_1_RKLINK_TX:
if (rate == _MHZ(200)) { /* RKX111_CPS_DCLK_RX_PRE_200M */
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* bus */
case RKX110_CPS_BUSCLK_RX_PRE:
if (rate == OSC_24M) {
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* gpio */
case RKX110_CPS_DCLK_RX_GPIO0:
case RKX110_CPS_DCLK_RX_GPIO1:
/* efuse */
case RKX110_CPS_RX_EFUSE:
/* pcs_ada */
case RKX110_CPS_PCS0_ADA:
case RKX110_CPS_PCS1_ADA:
return rate == OSC_24M ? 0 : HAL_INVAL;
case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
if (rate != _MHZ(200)) {
return HAL_INVAL;
}
HAL_CRU_ClkSetMux(hw, clkMux, 0);
return HAL_OK;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return HAL_INVAL;
}
if (pRate) {
div = HAL_DIV_ROUND_UP(pRate, rate);
}
if (clkDiv) {
overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
if (overMax) {
CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
__func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
div = CLK_DIV_GET_MAXDIV(clkDiv);
}
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
}
if (clkMux) {
HAL_CRU_ClkSetMux(hw, clkMux, mux);
}
return HAL_OK;
}
#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
static void RKX11x_HAL_CRU_Init_SSCG(struct hwclk *hw)
{
uint8_t down_spread = 1; /* 0: center spread */
uint8_t amplitude = 8; /* range: 0x00 - 0x1f */
#if RKX110_SSCG_CPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
#endif
#if RKX110_SSCG_RXPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
CRU_MSG("%s: RXPLL enable SSCG\n", hw->name);
#endif
}
#endif
static HAL_Status RKX11x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
/* gpio0_b7: iomux to clk_testout */
HAL_CRU_Write(hw, RKX110_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
/* Enable clock */
HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
/* Mux, div */
HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
return HAL_OK;
}
static HAL_Status RKX11x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
{
hw->cru_base = 0x0;
hw->sel_con0 = hw->cru_base + 0x100;
hw->gate_con0 = hw->cru_base + 0x300;
hw->softrst_con0 = hw->cru_base + 0x400;
hw->gbl_srst_fst = 0x0614;
hw->flags = 0;
hw->num_gate = 16 * 12;
hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
if (!hw->gate) {
return HAL_NOMEM;
}
strcat(hw->name, "<CRU.111@");
strcat(hw->name, xfer->name);
strcat(hw->name, ">");
/* Don't change order */
#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
RKX11x_HAL_CRU_Init_SSCG(hw);
#endif
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_CPLL, _MHZ(1200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_RXPLL, _MHZ(1188));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE, _MHZ(100));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_PMA2LINK2PCS_CM, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_DCLK_RX_PRE, _MHZ(400));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_RX_PRE_200M, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_D_DSI_0_REC, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_D_DSI_1_REC, _MHZ(200));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, _MHZ(150));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, _MHZ(150));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, _MHZ(400));
RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CFGCLK_MIPIRXPHY0, _MHZ(100));
HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
#if RKX110_TESTOUT_MUX >= 0
/* clk_testout support max 150M output, so set div=10 by default if not 24M */
RKX11x_HAL_CRU_InitTestout(hw, RKX110_CPS_TEST_CLKOUT, RKX110_TESTOUT_MUX,
RKX110_TESTOUT_MUX == 0 ? 1 : 10);
#endif
return HAL_OK;
}
PNAME(mux_24m_p) = { "xin24m" };
PNAME(mux_rxpll_cpll_p) = { "rxpll", "cpll" };
PNAME(mux_rxpll_cpll_24m_p) = { "rxpll", "cpll", "xin24m" };
PNAME(mux_rxpre0_24m_p) = { "xin24m", "busclk_rx_pre0" };
#define CAL_FREQ_REG 0xf00
#define CAL_FREQ_EN_REG 0xf04
static uint32_t RKX11x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
{
uint32_t clkMux = CLK_GET_MUX(RKX110_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX110_CPS_TEST_CLKOUT);
uint32_t freq, mhz;
uint8_t div = 10;
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
HAL_CRU_ClkSetMux(hw, clkMux, clk);
HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1); /* NOTE: 1: disable, 0: enable */
HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 0);
HAL_SleepMs(3);
freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1);
/* Fix accuracy */
if ((freq % 10) == 0x9) {
freq++;
}
freq *= (1000 * div);
/* If no external input, freq is close to 24M */
mhz = freq / 1000000;
if ((clk != 0) && (mhz == 23 || mhz == 24)) {
freq = 0;
}
return freq;
}
static struct clkTable rkx11x_clkTable[] = {
/* internal */
CLK_DECLARE_INT("rxpll", RKX110_CPS_PLL_RXPLL, mux_24m_p),
CLK_DECLARE_INT("cpll", RKX110_CPS_PLL_CPLL, mux_24m_p),
CLK_DECLARE_INT("dclk_rx_pre", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_dsi_0_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_dsi_1_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cam0_out2io", RKX110_CPS_CLK_CAM0_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cam1_out2io", RKX110_CPS_CLK_CAM1_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_cif_out2io", RKX110_CPS_CLK_CIF_OUT2IO, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("dclk_d_dsi_0_rec", RKX111_CPS_DCLK_D_DSI_0_REC, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("dclk_d_dsi_1_rec", RKX111_CPS_DCLK_D_DSI_1_REC, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_2x_lvds_rklink_tx", RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_i2s_src_rklink_tx", RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("ckref_mipirxphy0", RKX110_CPS_CKREF_MIPIRXPHY0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("ckref_mipirxphy1", RKX110_CPS_CKREF_MIPIRXPHY1, mux_rxpll_cpll_p),
CLK_DECLARE_INT("cfgclk_mipirxphy0", RKX110_CPS_CFGCLK_MIPIRXPHY0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("cfgclk_mipirxphy1", RKX110_CPS_CFGCLK_MIPIRXPHY1, mux_rxpll_cpll_p),
CLK_DECLARE_INT("busclk_rx_pre0", RKX110_CPS_BUSCLK_RX_PRE0, mux_rxpll_cpll_p),
CLK_DECLARE_INT("busclk_rx_pre", RKX110_CPS_BUSCLK_RX_PRE, mux_rxpre0_24m_p),
CLK_DECLARE_INT("dclk_rx_gpio0", RKX110_CPS_DCLK_RX_GPIO0, mux_24m_p),
CLK_DECLARE_INT("dclk_rx_gpio1", RKX110_CPS_DCLK_RX_GPIO1, mux_24m_p),
CLK_DECLARE_INT("rx_efuse", RKX110_CPS_RX_EFUSE, mux_24m_p),
CLK_DECLARE_INT("pcs0_ada", RKX110_CPS_PCS0_ADA, mux_24m_p),
CLK_DECLARE_INT("pcs1_ada", RKX110_CPS_PCS1_ADA, mux_24m_p),
CLK_DECLARE_INT("dclk_rx_pre_200m", RKX111_CPS_DCLK_RX_PRE_200M, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_lvds0_pattern_gen", RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, mux_rxpll_cpll_24m_p),
CLK_DECLARE_INT("clk_d_lvds1_pattern_gen", RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, mux_rxpll_cpll_24m_p),
/* external */
CLK_DECLARE_EXT("xin24m", 0, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("rxpclk_vicap_lvds", 3, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxbytehs_csihost0", 4, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxbytehs_csihost1", 5, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_d_rgb_rklink_tx", 6, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_lvds0", 7, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_lvds1", 8, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("pclkin_vicap_dvp_rx", 21, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("iclk_dsi0", 22, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("iclk_dsi1", 23, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("rxpclk_lvds_align", 24, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_8x_pma2pcs0", 25, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_8x_pma2pcs1", 26, RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx_cm", 7, "dclk_lvds0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds0_pattern_gen_en", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx_cm", 8, "dclk_lvds1", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_d_lvds1_pattern_gen_en", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 9, "clk_link_pcs1", RKX11x_HAL_CRU_ClkGetExtFreq),
{ /* sentinel */ },
};
struct clkOps rkx111_clk_ops =
{
.clkTable = rkx11x_clkTable,
.clkInit = RKX11x_HAL_CRU_Init,
.clkGetFreq = RKX11x_HAL_CRU_ClkGetFreq,
.clkSetFreq = RKX11x_HAL_CRU_ClkSetFreq,
.clkInitTestout = RKX11x_HAL_CRU_InitTestout,
};

View File

@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_RKX111_H
#include "cru_rkx110.h"
// RXCRU_SOFTRST_CON02(Offset:0x408)
#define RKX111_SRST_DRESETN_VICAP_CSI_LS 0x0000002E
// RXCRU_SOFTRST_CON05(Offset:0x414)
#define RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX 0x00000056
#define RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX 0x00000057
// RXCRU_SOFTRST_CON06(Offset:0x418)
#define RKX111_SRST_RESETN_D_LVDS0_PATTERN_GEN 0x00000066
#define RKX111_SRST_RESETN_D_LVDS1_PATTERN_GEN 0x00000067
// RXCRU_SOFTRST_CON11(Offset:0x42C)
#define RKX111_SRST_PRESETN_LBIST_ADA_RX 0x000000B1
// RXCRU_GATE_CON02(Offset:0x308)
#define RKX111_DCLK_RX_PRE_200M_GATE 0x0000002D
#define RKX111_DCLK_VICAP_CSI_LS_GATE 0x0000002E
// RXCRU_GATE_CON04(Offset:0x310)
#define RKX111_CLK_D_DSI_0_RKLINK_TX_PRE_GATE 0x00000044
#define RKX111_CLK_D_DSI_1_RKLINK_TX_PRE_GATE 0x00000045
#define RKX111_CLK_D_LVDS0_RKLINK_TX_GATE 0x00000047
#define RKX111_CLK_D_LVDS0_RKLINK_TX_PRE_GATE 0x00000048
#define RKX111_CLK_D_LVDS1_RKLINK_TX_GATE 0x00000049
#define RKX111_CLK_D_LVDS1_RKLINK_TX_PRE_GATE 0x0000004A
#define RKX111_DCLK_D_DSI_0_REC_GATE 0x0000004D
#define RKX111_DCLK_D_DSI_1_REC_GATE 0x0000004E
// RXCRU_GATE_CON05(Offset:0x314)
#define RKX111_DCLK_D_DSI_0_REC_RKLINK_TX_GATE 0x00000056
#define RKX111_DCLK_D_DSI_1_REC_RKLINK_TX_GATE 0x00000057
#define RKX111_CLK_D_DSI_0_RKLINK_TX_GATE 0x00000058
#define RKX111_CLK_D_DSI_1_RKLINK_TX_GATE 0x00000059
// RXCRU_GATE_CON06(Offset:0x318)
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_GATE 0x00000066
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_GATE 0x00000067
// RXCRU_GATE_CON11(Offset:0x32C)
#define RKX111_PCLK_LBIST_ADA_RX_GATE 0x000000B1
// RXCRU_CLKSEL_CON05(Offset:0x114)
#define RKX111_DCLK_D_DSI_0_REC_DIV 0x08000005
#define RKX111_DCLK_D_DSI_0_REC_SEL 0x020E0005
#define RKX111_DCLK_D_DSI_0_REC_SEL_CLK_RXPLL_MUX 0U
#define RKX111_DCLK_D_DSI_0_REC_SEL_CLK_CPLL_MUX 1U
#define RKX111_DCLK_D_DSI_0_REC_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON06(Offset:0x118)
#define RKX111_DCLK_D_DSI_1_REC_DIV 0x08000006
#define RKX111_DCLK_D_DSI_1_REC_SEL 0x020E0006
#define RKX111_DCLK_D_DSI_1_REC_SEL_CLK_RXPLL_MUX 0U
#define RKX111_DCLK_D_DSI_1_REC_SEL_CLK_CPLL_MUX 1U
#define RKX111_DCLK_D_DSI_1_REC_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON13(Offset:0x134)
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_DIV 0x0800000D
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL 0x020E000D
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_CLK_RXPLL_MUX 0U
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_CLK_CPLL_MUX 1U
#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON14(Offset:0x138)
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_DIV 0x0800000E
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL 0x020E000E
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_CLK_RXPLL_MUX 0U
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_CLK_CPLL_MUX 1U
#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON16(Offset:0x140)
#define RKX111_DCLK_RX_PRE_200M_DIV 0x06000010
#define RKX111_DCLK_RX_PRE_200M_SEL 0x02060010
#define RKX111_DCLK_RX_PRE_200M_SEL_CLK_RXPLL_MUX 0U
#define RKX111_DCLK_RX_PRE_200M_SEL_CLK_CPLL_MUX 1U
#define RKX111_DCLK_RX_PRE_200M_SEL_XIN_OSC0_FUNC 2U
// RXCRU_CLKSEL_CON17(Offset:0x144)
#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL 0x010C0011
#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL_CLK_D_DSI_0_RKLINK_TX_PRE 0U
#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL_CLK_D_DSI_0_PATTERN_GEN 1U
#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL 0x010D0011
#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL_CLK_D_DSI_1_RKLINK_TX_PRE 0U
#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL_CLK_D_DSI_1_PATTERN_GEN 1U
#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL 0x010E0011
#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL_CLK_D_LVDS0_RKLINK_TX_PRE 0U
#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL_CLK_D_LVDS0_PATTERN_GEN 1U
#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL 0x010F0011
#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL_CLK_D_LVDS1_RKLINK_TX_PRE 0U
#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL_CLK_D_LVDS1_PATTERN_GEN 1U
/* COMPOSITE */
#define RKX111_CPS_DCLK_RX_PRE_200M COMPOSITE_CLK(RKX111_DCLK_RX_PRE_200M_SEL, RKX111_DCLK_RX_PRE_200M_DIV)
/* lvds_pattern_gen => lvds_rklink */
#define RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN COMPOSITE_CLK(RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL, RKX111_CLK_D_LVDS0_PATTERN_GEN_DIV)
#define RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN COMPOSITE_CLK(RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL, RKX111_CLK_D_LVDS1_PATTERN_GEN_DIV)
#define RKX111_CPS_CLK_D_LVDS0_RKLINK_TX COMPOSITE_CLK(RKX111_CLK_D_LVDS0_RKLINK_TX_SEL, 0)
#define RKX111_CPS_CLK_D_LVDS1_RKLINK_TX COMPOSITE_CLK(RKX111_CLK_D_LVDS1_RKLINK_TX_SEL, 0)
/* dsi_rec => dsi_rklink */
#define RKX111_CPS_DCLK_D_DSI_0_REC COMPOSITE_CLK(RKX111_DCLK_D_DSI_0_REC_SEL, RKX111_DCLK_D_DSI_0_REC_DIV)
#define RKX111_CPS_DCLK_D_DSI_1_REC COMPOSITE_CLK(RKX111_DCLK_D_DSI_1_REC_SEL, RKX111_DCLK_D_DSI_1_REC_DIV)
#define RKX111_CPS_CLK_D_DSI_0_RKLINK_TX COMPOSITE_CLK(RKX111_CLK_D_DSI_0_RKLINK_TX_SEL, 0)
#define RKX111_CPS_CLK_D_DSI_1_RKLINK_TX COMPOSITE_CLK(RKX111_CLK_D_DSI_1_RKLINK_TX_SEL, 0)
#endif

View File

@@ -0,0 +1,599 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#include "cru_core.h"
#include "cru_rkx120.h"
/*
* [RKX120 CHIP]: TX
*
* ================= SECITON: Input clock from devices =========================
*
* ### 300M ###
* dclk_c_dvp_src
* dclk_d_dsi_src --|-- dclk_d_ds
* |-- dclk_d_dsi_pattern_gen
*
* clk_lvds0_src --|-- clk_lvds0
* |-- clk_lvds0_pattern_gen
*
* clk_lvds1_src --|-- clk_lvds1
* |-- clk_lvds1_pattern_gen
*
* dclk_rgc_src
*
*
* ### 200M ###
* clk_link_pcs0 --|-- clk_pma2pcs0 |-- clk_pma2link2pcs_link
* | |
* |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
* clk_link_pcs1 --| |
* |-- clk_pma2pcs1 |-- clk_pma2link2pcs_psc1
*
* clk_txbytehs_dsitx_csitx0
* clk_txbytehs_dsitx_csitx1
*
*
*
* ### 150M ###
*
* rxpclk_vicap_lvds
*
*
* ### 100M ###
*
* clk_2x_pma2pcs0
* clk_2x_pma2pcs1
*
*
* ### 50M ###
* clk_txesc_mipitxphy0
* clk_rxesc_dsitx
*
*/
#define RKX120_SSCG_TXPLL_EN 0
#define RKX120_SSCG_CPLL_EN 0
#define RKX120_TESTOUT_MUX -1 /* valid options: RKX120_TEST_CLKOUT_IOUT_SEL_? */
#define RKX120_GRF_GPIO0B_IOMUX_H 0x0101000c
#define GPIO0B7_TEST_CLKOUT 0x01c00080
#define I2S_122888_BEST_PRATE 393216000
#define I2S_112896_BEST_PRATE 756403200
static struct PLL_CONFIG PLL_TABLE[] = {
/* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
/* display */
RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
/* audio: 12.288M */
RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435), /* div=56, vco=2064.384M */
RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217), /* div=28, vco=1032.192M */
/* audio: 11.2896M */
RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340), /* div=42, vco=1896.6528M */
RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714), /* div=67, vco=1512.8064M */
RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088), /* div=50, vco=1128.96M */
{ /* sentinel */ },
};
static struct PLL_SETUP TXPLL_SETUP = {
.id = PLL_TXPLL,
.conOffset0 = 0x01000020,
.conOffset1 = 0x01000024,
.conOffset2 = 0x01000028,
.conOffset3 = 0x0100002c,
.modeOffset = 0x01000600,
.modeShift = 0, /* 0: slow-mode, 1: normal-mode */
.lockShift = 10,
.modeMask = 0x1,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX120_SSCG_TXPLL_EN,
};
static struct PLL_SETUP CPLL_SETUP = {
.id = PLL_CPLL,
.conOffset0 = 0x01000000,
.conOffset1 = 0x01000004,
.conOffset2 = 0x01000008,
.conOffset3 = 0x0100000c,
.modeOffset = 0x01000600,
.modeShift = 2,
.lockShift = 10,
.modeMask = 0x1 << 2,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX120_SSCG_CPLL_EN,
};
static uint32_t RKX12x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
uint32_t pRate = 0, freq = 0;
switch (clockName) {
case RKX120_CPS_PLL_TXPLL:
freq = HAL_CRU_GetPllFreq(hw, &TXPLL_SETUP);
hw->pllRate[RKX120_TXPLL] = freq;
return freq;
case RKX120_CPS_PLL_CPLL:
freq = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
hw->pllRate[RKX120_CPLL] = freq;
return freq;
/* link: 300M */
case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
/* csi: 50M */
case RKX120_CPS_CLK_TXESC_CSITX0:
case RKX120_CPS_CLK_TXESC_CSITX1:
/* i2s: 600M */
case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
/* pwm: 100M */
case RKX120_CPS_CLK_PWM_TX:
case RKX120_CPS_PCLKOUT_DVPTX:
freq = HAL_CRU_MuxGetFreq3(hw, clkMux,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], OSC_24M);
break;
/* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI0:
return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE);
/* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI1:
return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE);
/* pre-bus: 100M */
case RKX120_CPS_BUSCLK_TX_PRE0:
freq = HAL_CRU_MuxGetFreq2(hw, clkMux,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL]);
break;
/*
* bus: 100MHZ
*
* === TX_BUSCLK_TX_PRE gate children ===
*
* pclk_tx_cru
* pclk_tx_grf
* pclk_tx_gpio0/1
* pclk_tx_efuse
* pclk_mipi_grf_tx0/1
* pclk_tx_i2c2apb
* pclk_tx_i2c2apb_debug
* hclk_dvp_tx
* pclk_csitx0/1
* pclk_dsitx
* pclk_rklink_rx
* pclk_d_dsi_pattern_gen
* pclk_lvds{0, 1}_pattern_gen
* pclk_pcs0/1
* pclk_pcs{0,1}_ada
* pclk_mipitxphy0/1
* pclk_pwm_tx
* pclk_dft2apb
*/
case RKX120_CPS_BUSCLK_TX_PRE:
pRate = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
break;
/* gpio: 24M */
case RKX120_CPS_DCLK_TX_GPIO0:
case RKX120_CPS_DCLK_TX_GPIO1:
/* efuse: 24M */
case RKX120_CPS_CLK_TX_EFUSE:
/* pcs_ada: 24M */
case RKX120_CPS_CLK_PCS0_ADA:
case RKX120_CPS_CLK_PCS1_ADA:
/* capture_pwm: 24M */
case RKX120_CPS_CLK_CAPTURE_PWM_TX:
return OSC_24M;
case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
return _MHZ(200);
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return 0;
}
if (clkDiv) {
freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
}
return freq;
}
static HAL_Status RKX12x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
uint32_t mux = 0, div = 1;
uint32_t pRate = 0;
uint32_t maxDiv;
uint32_t pll;
uint8_t overMax;
HAL_Status ret = HAL_OK;
switch (clockName) {
case RKX120_CPS_PLL_TXPLL:
ret = HAL_CRU_SetPllFreq(hw, &TXPLL_SETUP, rate);
hw->pllRate[RKX120_TXPLL] = rate;
CRU_MSG("%s: TXPLL set rate: %d\n", hw->name, rate);
return ret;
case RKX120_CPS_PLL_CPLL:
ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
hw->pllRate[RKX120_CPLL] = rate;
CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
return ret;
/* link(dclk): Allowed to change PLL rate if need ! */
case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
/* i2s */
case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
if (DIV_NO_REM(hw->pllRate[RKX120_TXPLL], rate, maxDiv)) {
mux = 0;
pRate = hw->pllRate[RKX120_TXPLL];
} else if (DIV_NO_REM(hw->pllRate[RKX120_CPLL], rate, maxDiv)) {
mux = 1;
pRate = hw->pllRate[RKX120_CPLL];
} else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
mux = 2;
pRate = OSC_24M;
} else {
if (clockName == RKX120_CPS_CLK_I2S_SRC_RKLINK_RX) {
pll = RKX120_CPS_PLL_CPLL;
if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_122888_BEST_PRATE;
} else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_112896_BEST_PRATE;
}
} else {
pll = RKX120_CPS_PLL_TXPLL;
}
/* PLL change closest new rate <= 1200M if need */
if (!pRate) {
pRate = (_MHZ(1200) / rate) * rate;
}
ret = RKX12x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
if (ret != HAL_OK) {
return ret;
}
/* if success, continue to set divider */
}
break;
/* csi */
case RKX120_CPS_CLK_TXESC_CSITX0:
case RKX120_CPS_CLK_TXESC_CSITX1:
/* pwm */
case RKX120_CPS_CLK_PWM_TX:
case RKX120_CPS_PCLKOUT_DVPTX:
mux = HAL_CRU_RoundFreqGetMux3(hw, rate,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], OSC_24M, &pRate);
break;
/* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI0:
return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, rate);
/* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI1:
return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, rate);
/* pre-bus */
case RKX120_CPS_BUSCLK_TX_PRE0:
mux = HAL_CRU_RoundFreqGetMux2(hw, rate,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], &pRate);
break;
case RKX120_CPS_BUSCLK_TX_PRE:
if (rate == OSC_24M) {
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* gpio: 24M */
case RKX120_CPS_DCLK_TX_GPIO0:
case RKX120_CPS_DCLK_TX_GPIO1:
/* efuse: 24M */
case RKX120_CPS_CLK_TX_EFUSE:
/* pcs_ada: 24M */
case RKX120_CPS_CLK_PCS0_ADA:
case RKX120_CPS_CLK_PCS1_ADA:
/* capture_pwm: 24M */
case RKX120_CPS_CLK_CAPTURE_PWM_TX:
return rate == OSC_24M ? 0 : HAL_INVAL;
case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
if (rate != _MHZ(200)) {
return HAL_INVAL;
}
HAL_CRU_ClkSetMux(hw, clkMux, 0);
return HAL_OK;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return HAL_INVAL;
}
if (pRate) {
div = HAL_DIV_ROUND_UP(pRate, rate);
}
if (clkDiv) {
overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
if (overMax) {
CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
__func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
div = CLK_DIV_GET_MAXDIV(clkDiv);
}
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
}
if (clkMux) {
HAL_CRU_ClkSetMux(hw, clkMux, mux);
}
return HAL_OK;
}
#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
static void RKX12x_HAL_CRU_Init_SSCG(struct hwclk *hw)
{
uint8_t down_spread = 1; /* 0: center spread */
uint8_t amplitude = 8; /* range: 0x00 - 0x1f */
#if RKX120_SSCG_CPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
#endif
#if RKX120_SSCG_TXPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
CRU_MSG("%s: TXPLL enable SSCG\n", hw->name);
#endif
}
#endif
static HAL_Status RKX12x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue)
{
uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
/* gpio0_b7: iomux to clk_testout */
HAL_CRU_Write(hw, RKX120_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
/* Enable clock */
HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
/* Mux, div */
HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
return HAL_OK;
}
static HAL_Status RKX12x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
{
hw->cru_base = 0x01000000;
hw->sel_con0 = hw->cru_base + 0x100;
hw->gate_con0 = hw->cru_base + 0x300;
hw->softrst_con0 = hw->cru_base + 0x400;
hw->gbl_srst_fst = 0x0614;
hw->flags = 0;
hw->num_gate = 16 * 12;
hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
if (!hw->gate) {
return HAL_NOMEM;
}
strcat(hw->name, "<CRU.120@");
strcat(hw->name, xfer->name);
strcat(hw->name, ">");
/* Don't change order */
#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
RKX12x_HAL_CRU_Init_SSCG(hw);
#endif
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_CPLL, _MHZ(1200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_TXPLL, _MHZ(1188));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE, _MHZ(24));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PMA2PCS2LINK_CM, _MHZ(200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, _MHZ(300));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PWM_TX, _MHZ(24));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX0, _MHZ(20));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX1, _MHZ(20));
/* Must be the same rate as RKX110_CPS_DCLK_RX_PRE when camera mode */
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, _MHZ(200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, _MHZ(200));
HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
#if RKX120_TESTOUT_MUX >= 0
/* clk_testout support max 150M output, so set div=10 by default if not 24M */
RKX12x_HAL_CRU_InitTestout(hw, RKX120_CPS_TEST_CLKOUT, RKX120_TESTOUT_MUX,
RKX120_TESTOUT_MUX == 0 ? 1 : 10);
#endif
return HAL_OK;
}
PNAME(mux_24m_p) = { "xin24m" };
PNAME(mux_txpll_cpll_p) = { "txpll", "cpll" };
PNAME(mux_txpll_cpll_24m_p) = { "txpll", "cpll", "xin24m" };
PNAME(mux_24m_txpre0_p) = { "xin24m", "busclk_tx_pre0" };
#define CAL_FREQ_REG 0x01000f00
static uint32_t RKX12x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
{
uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
uint32_t freq, mhz;
uint8_t div = 10;
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
HAL_CRU_ClkSetMux(hw, clkMux, 0);
HAL_SleepMs(2);
HAL_CRU_ClkSetMux(hw, clkMux, clk);
HAL_SleepMs(2);
freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
/* Fix accuracy */
if ((freq % 10) == 0x9) {
freq++;
}
freq *= (1000 * div);
/* If no external input, freq is close to 24M */
mhz = freq / 1000000;
if ((clk != 0) && (mhz == 23 || mhz == 24)) {
freq = 0;
}
return freq;
}
static struct clkTable rkx12x_clkTable[] = {
/* internal */
CLK_DECLARE_INT("txpll", RKX120_CPS_PLL_TXPLL, mux_24m_p),
CLK_DECLARE_INT("cpll", RKX120_CPS_PLL_CPLL, mux_24m_p),
CLK_DECLARE_INT("e0_clk_rklink_rx_pre", RKX120_CPS_E0_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("e1_clk_rklink_rx_pre", RKX120_CPS_E1_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("iclk_c_csi0", RKX120_CPS_ICLK_C_CSI0, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("iclk_c_csi1", RKX120_CPS_ICLK_C_CSI1, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_txesc_csitx0", RKX120_CPS_CLK_TXESC_CSITX0, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_txesc_csitx1", RKX120_CPS_CLK_TXESC_CSITX1, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_i2s_src_rklink_rx", RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_pwm_tx", RKX120_CPS_CLK_PWM_TX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("pclkout_dvptx", RKX120_CPS_PCLKOUT_DVPTX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("busclk_tx_pre0", RKX120_CPS_BUSCLK_TX_PRE0, mux_txpll_cpll_p),
CLK_DECLARE_INT("busclk_tx_pre", RKX120_CPS_BUSCLK_TX_PRE, mux_24m_txpre0_p),
CLK_DECLARE_INT("dclk_tx_gpio0", RKX120_CPS_DCLK_TX_GPIO0, mux_24m_p),
CLK_DECLARE_INT("dclk_tx_gpio1", RKX120_CPS_DCLK_TX_GPIO1, mux_24m_p),
CLK_DECLARE_INT("clk_tx_efuse", RKX120_CPS_CLK_TX_EFUSE, mux_24m_p),
CLK_DECLARE_INT("clk_pcs0_ada", RKX120_CPS_CLK_PCS0_ADA, mux_24m_p),
CLK_DECLARE_INT("clk_pcs1_ada", RKX120_CPS_CLK_PCS1_ADA, mux_24m_p),
CLK_DECLARE_INT("clk_capture_pwm_tx", RKX120_CPS_CLK_CAPTURE_PWM_TX, mux_24m_p),
/* external */
CLK_DECLARE_EXT("xin24m", 0, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_csitx0", 3, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_csitx1", 5, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_dsitx", 7, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxesc_dsitx", 8, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_pmarx0_pixel", 11, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_pmarx1_pixel", 12, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy0_lvds", 13, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy0_pixel", 14, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy1_lvds", 15, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy1_pixel", 16, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_dsitx_csitx0", 23, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_c_dvp_src", 24, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_d_dsi_src", 25, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_lvds0_src", 26, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_lvds1_src", 27, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_rgb_src", 28, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_2x_pma2pcs0", 29, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_2x_pma2pcs1", 30, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txesc_mipitxphy0", 31, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("dclk_d_ds", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("dclk_d_dsi_pattern_gen", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds0", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds0_pattern_gen", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds1", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds1_pattern_gen", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 10, "clk_link_pcs1", RKX12x_HAL_CRU_ClkGetExtFreq),
{ /* sentinel */ },
};
struct clkOps rkx120_clk_ops =
{
.clkTable = rkx12x_clkTable,
.clkInit = RKX12x_HAL_CRU_Init,
.clkGetFreq = RKX12x_HAL_CRU_ClkGetFreq,
.clkSetFreq = RKX12x_HAL_CRU_ClkSetFreq,
.clkInitTestout = RKX12x_HAL_CRU_InitTestout,
};

View File

@@ -0,0 +1,305 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_RKX120_H
#include "cru_core.h"
// ======================== TXCRU module definition START ======================
// TXCRU_SOFTRST_CON01(Offset:0x404)
#define RKX120_SRST_PRESETN_TX_CRU 0x00000010
#define RKX120_SRST_PRESETN_TX_GRF 0x00000011
#define RKX120_SRST_PRESETN_TX_GPIO0 0x00000012
#define RKX120_SRST_DRESETN_TX_GPIO0 0x00000013
#define RKX120_SRST_PRESETN_TX_GPIO1 0x00000014
#define RKX120_SRST_DRESETN_TX_GPIO1 0x00000015
#define RKX120_SRST_PRESETN_TX_EFUSE 0x00000016
#define RKX120_SRST_RESETN_TX_EFUSE 0x00000017
#define RKX120_SRST_PRESETN_MIPI_GRF_TX0 0x0000001A
#define RKX120_SRST_PRESETN_MIPI_GRF_TX1 0x0000001B
#define RKX120_SRST_PRESETN_TX_I2C2APB 0x0000001E
#define RKX120_SRST_PRESETN_TX_I2C2APB_DEBUG 0x0000001F
// TXCRU_SOFTRST_CON02(Offset:0x408)
#define RKX120_SRST_HRESETN_DVP_TX 0x00000020
// TXCRU_SOFTRST_CON03(Offset:0x40C)
#define RKX120_SRST_PRESETN_CSITX0 0x00000030
#define RKX120_SRST_RESETN_TXBYTEHS_CSITX0 0x00000031
#define RKX120_SRST_RESETN_TXESC_CSITX0 0x00000032
#define RKX120_SRST_PRESETN_CSITX1 0x00000034
#define RKX120_SRST_RESETN_TXBYTEHS_CSITX1 0x00000035
#define RKX120_SRST_RESETN_TXESC_CSITX1 0x00000036
#define RKX120_SRST_PRESETN_DSITX 0x00000038
// TXCRU_SOFTRST_CON04(Offset:0x410)
#define RKX120_SRST_PRESETN_RKLINK_RX 0x00000040
#define RKX120_SRST_RESETN_I2S_SRC_RKLINK_RX 0x00000041
#define RKX120_SRST_RESETN_E0_RKLINK_RX 0x00000045
#define RKX120_SRST_IRESETN_C_CSI0 0x00000046
#define RKX120_SRST_RESETN_E1_RKLINK_RX 0x00000049
#define RKX120_SRST_IRESETN_C_CSI1 0x0000004A
// TXCRU_SOFTRST_CON05(Offset:0x414)
#define RKX120_SRST_DRESETN_C_DVP 0x00000051
#define RKX120_SRST_RESETN_LVDS0 0x0000005B
#define RKX120_SRST_RESETN_LVDS1 0x0000005D
// TXCRU_SOFTRST_CON06(Offset:0x418)
#define RKX120_SRST_RESETN_PMA2PCS2LINK_LINK 0x00000061
#define RKX120_SRST_RESETN_PMA2PCS2LINK_PCS0 0x00000062
#define RKX120_SRST_RESETN_PMA2PCS2LINK_PCS1 0x00000063
#define RKX120_SRST_PRESETN_D_DSI_PATTERN_GEN 0x00000064
#define RKX120_SRST_PRESETN_LVDS0_PATTERN_GEN 0x00000065
#define RKX120_SRST_PRESETN_LVDS1_PATTERN_GEN 0x00000066
#define RKX120_SRST_DRESETN_D_DSI_PATTERN_GEN 0x00000067
#define RKX120_SRST_RESETN_LVDS0_PATTERN_GEN 0x00000068
#define RKX120_SRST_RESETN_LVDS1_PATTERN_GEN 0x00000069
// TXCRU_SOFTRST_CON07(Offset:0x41C)
#define RKX120_SRST_PRESETN_PCS0 0x00000070
#define RKX120_SRST_RESETN_2X_PMA2PCS0 0x00000071
#define RKX120_SRST_RESETN_LINK_PCS0 0x00000073
#define RKX120_SRST_PRESETN_PCS0_ADA 0x00000074
#define RKX120_SRST_RESETN_PCS0_ADA 0x00000075
// TXCRU_SOFTRST_CON08(Offset:0x420)
#define RKX120_SRST_PRESETN_PCS1 0x00000080
#define RKX120_SRST_RESETN_2X_PMA2PCS1 0x00000081
#define RKX120_SRST_RESETN_LINK_PCS1 0x00000083
#define RKX120_SRST_PRESETN_PCS1_ADA 0x00000084
#define RKX120_SRST_RESETN_PCS1_ADA 0x00000085
// TXCRU_SOFTRST_CON09(Offset:0x424)
#define RKX120_SRST_PRESETN_DVPTX 0x00000090
#define RKX120_SRST_PRESETN_MIPITXPHY0 0x00000098
#define RKX120_SRST_RESETN_MIPITXPHY0 0x00000099
#define RKX120_SRST_PRESETN_MIPITXPHY1 0x0000009A
#define RKX120_SRST_RESETN_MIPITXPHY1 0x0000009B
// TXCRU_SOFTRST_CON10(Offset:0x428)
#define RKX120_SRST_PRESETN_PWM_TX 0x000000A0
#define RKX120_SRST_RESETN_PWM_TX 0x000000A1
// TXCRU_SOFTRST_CON11(Offset:0x42C)
#define RKX120_SRST_PRESETN_DFT2APB 0x000000B0
// TXCRU_GATE_CON00(Offset:0x300)
#define RKX120_CLK_TESTOUT_TOP_GATE 0x00000000
#define RKX120_BUSCLK_TX_PRE0_GATE 0x00000001
#define RKX120_BUSCLK_TX_PRE_GATE 0x00000002
// TXCRU_GATE_CON01(Offset:0x304)
#define RKX120_PCLK_TX_CRU_GATE 0x00000010
#define RKX120_PCLK_TX_GRF_GATE 0x00000011
#define RKX120_PCLK_TX_GPIO0_GATE 0x00000012
#define RKX120_DCLK_TX_GPIO0_GATE 0x00000013
#define RKX120_PCLK_TX_GPIO1_GATE 0x00000014
#define RKX120_DCLK_TX_GPIO1_GATE 0x00000015
#define RKX120_PCLK_TX_EFUSE_GATE 0x00000016
#define RKX120_CLK_TX_EFUSE_GATE 0x00000017
#define RKX120_PCLK_MIPI_GRF_TX0_GATE 0x0000001A
#define RKX120_PCLK_MIPI_GRF_TX1_GATE 0x0000001B
#define RKX120_PCLK_TX_I2C2APB_GATE 0x0000001E
#define RKX120_PCLK_TX_I2C2APB_DEBUG_GATE 0x0000001F
// TXCRU_GATE_CON02(Offset:0x308)
#define RKX120_HCLK_DVP_TX_GATE 0x00000020
// TXCRU_GATE_CON03(Offset:0x30C)
#define RKX120_PCLK_CSITX0_GATE 0x00000030
#define RKX120_CLK_TXBYTEHS_DSITX_CSITX0_DFT_GATE 0x00000031
#define RKX120_CLK_TXESC_CSITX0_GATE 0x00000032
#define RKX120_PCLK_CSITX1_GATE 0x00000034
#define RKX120_CLK_TXBYTEHS_CSITX1_DFT_GATE 0x00000035
#define RKX120_CLK_TXESC_CSITX1_GATE 0x00000036
#define RKX120_PCLK_DSITX_GATE 0x00000038
#define RKX120_CLK_RXESC_DSITX_DFT_GATE 0x0000003A
// TXCRU_GATE_CON04(Offset:0x310)
#define RKX120_PCLK_RKLINK_RX_GATE 0x00000040
#define RKX120_CLK_I2S_SRC_RKLINK_RX_GATE 0x00000041
#define RKX120_E0_CLK_RKLINK_RX_PRE_GATE 0x00000044
#define RKX120_E0_CLK_RKLINK_RX_GATE 0x00000045
#define RKX120_ICLK_C_CSI0_GATE 0x00000046
#define RKX120_E1_CLK_RKLINK_RX_PRE_GATE 0x00000048
#define RKX120_E1_CLK_RKLINK_RX_GATE 0x00000049
#define RKX120_ICLK_C_CSI1_GATE 0x0000004A
// TXCRU_GATE_CON05(Offset:0x314)
#define RKX120_DCLK_RGB_GATE 0x00000050
#define RKX120_DCLK_C_DVP_GATE 0x00000051
#define RKX120_DCLK_D_DSI_CM_GATE 0x00000058
#define RKX120_DCLK_D_DSI_GATE 0x00000059
#define RKX120_CLK_LVDS0_CM_GATE 0x0000005A
#define RKX120_CLK_LVDS0_GATE 0x0000005B
#define RKX120_CLK_LVDS1_CM_GATE 0x0000005C
#define RKX120_CLK_LVDS1_GATE 0x0000005D
// TXCRU_GATE_CON06(Offset:0x318)
#define RKX120_CLK_PMA2PCS2LINK_CM_GATE 0x00000060
#define RKX120_CLK_PMA2PCS2LINK_LINK_GATE 0x00000061
#define RKX120_CLK_PMA2PCS2LINK_PCS0_GATE 0x00000062
#define RKX120_CLK_PMA2PCS2LINK_PCS1_GATE 0x00000063
#define RKX120_PCLK_D_DSI_PATTERN_GEN_GATE 0x00000064
#define RKX120_PCLK_LVDS0_PATTERN_GEN_GATE 0x00000065
#define RKX120_PCLK_LVDS1_PATTERN_GEN_GATE 0x00000066
#define RKX120_DCLK_D_DSI_PATTERN_GEN_GATE 0x00000067
#define RKX120_CLK_LVDS0_PATTERN_GEN_GATE 0x00000068
#define RKX120_CLK_LVDS1_PATTERN_GEN_GATE 0x00000069
// TXCRU_GATE_CON07(Offset:0x31C)
#define RKX120_PCLK_PCS0_GATE 0x00000070
#define RKX120_CLK_2X_PMA2PCS0_DFT_GATE 0x00000071
#define RKX120_CLK_LINK_PCS0_DFT_GATE 0x00000072
#define RKX120_CLK_LINK_PCS0_GATE 0x00000073
#define RKX120_PCLK_PCS0_ADA_GATE 0x00000074
#define RKX120_CLK_PCS0_ADA_GATE 0x00000075
// TXCRU_GATE_CON08(Offset:0x320)
#define RKX120_PCLK_PCS1_GATE 0x00000080
#define RKX120_CLK_2X_PMA2PCS1_DFT_GATE 0x00000081
#define RKX120_CLK_LINK_PCS1_DFT_GATE 0x00000082
#define RKX120_CLK_LINK_PCS1_GATE 0x00000083
#define RKX120_PCLK_PCS1_ADA_GATE 0x00000084
#define RKX120_CLK_PCS1_ADA_GATE 0x00000085
// TXCRU_GATE_CON09(Offset:0x324)
#define RKX120_PCLKOUT_DVPTX_GATE 0x00000090
#define RKX120_PCLK_MIPITXPHY0_GATE 0x00000098
#define RKX120_PCLK_MIPITXPHY1_GATE 0x0000009A
// TXCRU_GATE_CON10(Offset:0x328)
#define RKX120_PCLK_PWM_TX_GATE 0x000000A0
#define RKX120_CLK_PWM_TX_GATE 0x000000A1
#define RKX120_CLK_CAPTURE_PWM_TX_GATE 0x000000A2
#define RKX120_CLK_TXESC_MIPITXPHY0_GATE 0x000000A8
// TXCRU_GATE_CON11(Offset:0x32C)
#define RKX120_PCLK_DFT2APB_GATE 0x000000B0
// TXCRU_CLKSEL_CON00(Offset:0x100)
#define RKX120_TEST_CLKOUT_IOUT_DIV 0x08000000
#define RKX120_TEST_CLKOUT_IOUT_SEL 0x05080000
#define RKX120_TEST_CLKOUT_IOUT_SEL_XIN_OSC0_FUNC 0U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXPLL_MUX 1U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_CPLL_MUX 2U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_CSITX0 3U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_CSITX0 4U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_CSITX1 5U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_CSITX1 6U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_DSITX 7U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_RXESC_DSITX 8U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS0 9U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS1 10U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_PMARX0_PIXEL 11U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_PMARX1_PIXEL 12U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY0_LVDS 13U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY0_PIXEL 14U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY1_LVDS 15U
#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY1_PIXEL 16U
#define RKX120_TEST_CLKOUT_IOUT_SEL_BUSCLK_TX_PRE 17U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_I2S_SRC_RKLINK_RX 20U
#define RKX120_TEST_CLKOUT_IOUT_SEL_E0_CLK_RKLINK_RX_PRE 21U
#define RKX120_TEST_CLKOUT_IOUT_SEL_PCLKOUT_DVPTX 22U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_DSITX_CSITX0 23U
#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_C_DVP_SRC 24U
#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_SRC 25U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LVDS0_SRC 26U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LVDS1_SRC 27U
#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_RGB_SRC 28U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_2X_PMA2PCS0 29U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_2X_PMA2PCS1 30U
#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_MIPITXPHY0 31U
// TXCRU_CLKSEL_CON01(Offset:0x104)
#define RKX120_BUSCLK_TX_PRE0_DIV 0x06000001
#define RKX120_BUSCLK_TX_PRE0_SEL 0x01070001
#define RKX120_BUSCLK_TX_PRE0_SEL_CLK_TXPLL_MUX 0U
#define RKX120_BUSCLK_TX_PRE0_SEL_CLK_CPLL_MUX 1U
#define RKX120_BUSCLK_TX_PRE_SEL 0x01080001
#define RKX120_BUSCLK_TX_PRE_SEL_XIN_OSC0_FUNC 0U
#define RKX120_BUSCLK_TX_PRE_SEL_BUSCLK_TX_PRE0 1U
// TXCRU_CLKSEL_CON03(Offset:0x10C)
#define RKX120_CLK_TXESC_CSITX0_DIV 0x08000003
#define RKX120_CLK_TXESC_CSITX0_SEL 0x020E0003
#define RKX120_CLK_TXESC_CSITX0_SEL_CLK_TXPLL_MUX 0U
#define RKX120_CLK_TXESC_CSITX0_SEL_CLK_CPLL_MUX 1U
#define RKX120_CLK_TXESC_CSITX0_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON04(Offset:0x110)
#define RKX120_CLK_TXESC_CSITX1_DIV 0x08000004
#define RKX120_CLK_TXESC_CSITX1_SEL 0x020E0004
#define RKX120_CLK_TXESC_CSITX1_SEL_CLK_TXPLL_MUX 0U
#define RKX120_CLK_TXESC_CSITX1_SEL_CLK_CPLL_MUX 1U
#define RKX120_CLK_TXESC_CSITX1_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON05(Offset:0x114)
#define RKX120_CLK_I2S_SRC_RKLINK_RX_DIV 0x08000005
#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL 0x020E0005
#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_CLK_TXPLL_MUX 0U
#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_CLK_CPLL_MUX 1U
#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON06(Offset:0x118)
#define RKX120_E0_CLK_RKLINK_RX_PRE_DIV 0x08000006
#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL 0x020E0006
#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_CLK_TXPLL_MUX 0U
#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_CLK_CPLL_MUX 1U
#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON07(Offset:0x11C)
#define RKX120_E1_CLK_RKLINK_RX_PRE_DIV 0x08000007
#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL 0x020E0007
#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_CLK_TXPLL_MUX 0U
#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_CLK_CPLL_MUX 1U
#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON08(Offset:0x120)
#define RKX120_CLK_PMA2PCS2LINK_CM_SEL 0x01000008
#define RKX120_CLK_PMA2PCS2LINK_CM_SEL_CLK_LINK_PCS0_DFT 0U
#define RKX120_CLK_PMA2PCS2LINK_CM_SEL_CLK_LINK_PCS1_DFT 1U
// TXCRU_CLKSEL_CON10(Offset:0x128)
#define RKX120_CLK_PWM_TX_DIV 0x0800000A
#define RKX120_CLK_PWM_TX_SEL 0x020E000A
#define RKX120_CLK_PWM_TX_SEL_CLK_TXPLL_MUX 0U
#define RKX120_CLK_PWM_TX_SEL_CLK_CPLL_MUX 1U
#define RKX120_CLK_PWM_TX_SEL_XIN_OSC0_FUNC 2U
// TXCRU_CLKSEL_CON12(Offset:0x130)
#define RKX120_PCLKOUT_DVPTX_DIV 0x0800000C
#define RKX120_PCLKOUT_DVPTX_SEL 0x020E000C
#define RKX120_PCLKOUT_DVPTX_SEL_CLK_TXPLL_MUX 0U
#define RKX120_PCLKOUT_DVPTX_SEL_CLK_CPLL_MUX 1U
#define RKX120_PCLKOUT_DVPTX_SEL_XIN_OSC0_FUNC 2U
// ======================== TXCRU module definition END ========================
#define RKX120_CPS_INVAL 0
#define RKX120_CPS_PLL_CPLL 1
#define RKX120_CPS_PLL_TXPLL 2
#define RKX120_CPS_DCLK_TX_GPIO0 3
#define RKX120_CPS_DCLK_TX_GPIO1 4
#define RKX120_CPS_CLK_TX_EFUSE 5
#define RKX120_CPS_CLK_PCS0_ADA 6
#define RKX120_CPS_CLK_PCS1_ADA 7
#define RKX120_CPS_CLK_CAPTURE_PWM_TX 8
#define RKX120_CPS_ICLK_C_CSI0 9
#define RKX120_CPS_ICLK_C_CSI1 10
#define RKX120_CPS_CLK_TXESC_CSITX0 COMPOSITE_CLK(RKX120_CLK_TXESC_CSITX0_SEL, RKX120_CLK_TXESC_CSITX0_DIV)
#define RKX120_CPS_CLK_TXESC_CSITX1 COMPOSITE_CLK(RKX120_CLK_TXESC_CSITX1_SEL, RKX120_CLK_TXESC_CSITX1_DIV)
#define RKX120_CPS_CLK_I2S_SRC_RKLINK_RX COMPOSITE_CLK(RKX120_CLK_I2S_SRC_RKLINK_RX_SEL, RKX120_CLK_I2S_SRC_RKLINK_RX_DIV)
#define RKX120_CPS_E0_CLK_RKLINK_RX_PRE COMPOSITE_CLK(RKX120_E0_CLK_RKLINK_RX_PRE_SEL, RKX120_E0_CLK_RKLINK_RX_PRE_DIV)
#define RKX120_CPS_E1_CLK_RKLINK_RX_PRE COMPOSITE_CLK(RKX120_E1_CLK_RKLINK_RX_PRE_SEL, RKX120_E1_CLK_RKLINK_RX_PRE_DIV)
#define RKX120_CPS_CLK_PMA2PCS2LINK_CM COMPOSITE_CLK(RKX120_CLK_PMA2PCS2LINK_CM_SEL, 0)
#define RKX120_CPS_PCLKOUT_DVPTX COMPOSITE_CLK(RKX120_PCLKOUT_DVPTX_SEL, RKX120_PCLKOUT_DVPTX_DIV)
#define RKX120_CPS_CLK_PWM_TX COMPOSITE_CLK(RKX120_CLK_PWM_TX_SEL, RKX120_CLK_PWM_TX_DIV)
#define RKX120_CPS_BUSCLK_TX_PRE0 COMPOSITE_CLK(RKX120_BUSCLK_TX_PRE0_SEL, RKX120_BUSCLK_TX_PRE0_DIV)
#define RKX120_CPS_BUSCLK_TX_PRE COMPOSITE_CLK(RKX120_BUSCLK_TX_PRE_SEL, 0)
#define RKX120_CPS_TEST_CLKOUT COMPOSITE_CLK(RKX120_TEST_CLKOUT_IOUT_SEL, RKX120_TEST_CLKOUT_IOUT_DIV)
#endif

View File

@@ -0,0 +1,626 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#include "cru_core.h"
#include "cru_rkx121.h"
/*
* [RKX121 CHIP]: TX
*
* ================= SECITON: Input clock from devices =========================
*
* ### 300M ###
* dclk_c_dvp_src
* dclk_d_dsi_src --|-- dclk_d_ds
* |-- dclk_d_dsi_pattern_gen
*
* clk_lvds0_src --|-- clk_lvds0
* |-- clk_lvds0_pattern_gen
*
* clk_lvds1_src --|-- clk_lvds1
* |-- clk_lvds1_pattern_gen
*
* dclk_rgc_src
*
*
* ### 200M ###
* clk_link_pcs0 --|-- clk_pma2pcs0 |-- clk_pma2link2pcs_link
* | |
* |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
* clk_link_pcs1 --| |
* |-- clk_pma2pcs1 |-- clk_pma2link2pcs_psc1
*
* clk_txbytehs_dsitx_csitx0
* clk_txbytehs_dsitx_csitx1
*
*
*
* ### 150M ###
*
* rxpclk_vicap_lvds
*
*
* ### 100M ###
*
* clk_2x_pma2pcs0
* clk_2x_pma2pcs1
*
*
* ### 50M ###
* clk_txesc_mipitxphy0
* clk_rxesc_dsitx
*
*/
#define RKX120_SSCG_TXPLL_EN 0
#define RKX120_SSCG_CPLL_EN 0
#define RKX120_TESTOUT_MUX -1 /* valid options: RKX120_TEST_CLKOUT_IOUT_SEL_? */
#define RKX120_GRF_GPIO0B_IOMUX_H 0x0101000c
#define GPIO0B7_TEST_CLKOUT 0x01c00080
#define I2S_122888_BEST_PRATE 393216000
#define I2S_112896_BEST_PRATE 756403200
static struct PLL_CONFIG PLL_TABLE[] = {
/* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
/* display */
RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
/* audio: 12.288M */
RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435), /* div=56, vco=2064.384M */
RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217), /* div=28, vco=1032.192M */
/* audio: 11.2896M */
RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340), /* div=42, vco=1896.6528M */
RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714), /* div=67, vco=1512.8064M */
RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088), /* div=50, vco=1128.96M */
{ /* sentinel */ },
};
static struct PLL_SETUP TXPLL_SETUP = {
.id = PLL_TXPLL,
.conOffset0 = 0x01000020,
.conOffset1 = 0x01000024,
.conOffset2 = 0x01000028,
.conOffset3 = 0x0100002c,
.modeOffset = 0x01000600,
.modeShift = 0, /* 0: slow-mode, 1: normal-mode */
.lockShift = 10,
.modeMask = 0x1,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX120_SSCG_TXPLL_EN,
};
static struct PLL_SETUP CPLL_SETUP = {
.id = PLL_CPLL,
.conOffset0 = 0x01000000,
.conOffset1 = 0x01000004,
.conOffset2 = 0x01000008,
.conOffset3 = 0x0100000c,
.modeOffset = 0x01000600,
.modeShift = 2,
.lockShift = 10,
.modeMask = 0x1 << 2,
.rateTable = PLL_TABLE,
.minRefdiv = 1,
.maxRefdiv = 2,
.minVco = _MHZ(375),
.maxVco = _MHZ(2400),
.minFout = _MHZ(24),
.maxFout = _MHZ(1200),
.sscgEn = RKX120_SSCG_CPLL_EN,
};
static uint32_t RKX12x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
uint32_t pRate = 0, freq = 0;
uint32_t pRate0 = 0, pRate1 = 0;
switch (clockName) {
case RKX120_CPS_PLL_TXPLL:
freq = HAL_CRU_GetPllFreq(hw, &TXPLL_SETUP);
hw->pllRate[RKX120_TXPLL] = freq;
return freq;
case RKX120_CPS_PLL_CPLL:
freq = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
hw->pllRate[RKX120_CPLL] = freq;
return freq;
/* lvds: 200M */
case RKX121_CPS_CLK_LVDS_C_LVDS_TX:
/* link: 300M */
case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
/* csi: 50M */
case RKX120_CPS_CLK_TXESC_CSITX0:
case RKX120_CPS_CLK_TXESC_CSITX1:
/* i2s: 600M */
case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
/* pwm: 100M */
case RKX120_CPS_CLK_PWM_TX:
case RKX120_CPS_PCLKOUT_DVPTX:
freq = HAL_CRU_MuxGetFreq3(hw, clkMux,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], OSC_24M);
break;
/* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI0:
return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE);
/* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI1:
return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE);
case RKX121_CPS_CLK_LVDS0:
case RKX121_CPS_CLK_LVDS1:
pRate0 = _MHZ(150); /* input clock: clk_lvds1_cm */
pRate1 = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
break;
/* pre-bus: 100M */
case RKX120_CPS_BUSCLK_TX_PRE0:
freq = HAL_CRU_MuxGetFreq2(hw, clkMux,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL]);
break;
/*
* bus: 100MHZ
*
* === TX_BUSCLK_TX_PRE gate children ===
*
* pclk_tx_cru
* pclk_tx_grf
* pclk_tx_gpio0/1
* pclk_tx_efuse
* pclk_mipi_grf_tx0/1
* pclk_tx_i2c2apb
* pclk_tx_i2c2apb_debug
* hclk_dvp_tx
* pclk_csitx0/1
* pclk_dsitx
* pclk_rklink_rx
* pclk_d_dsi_pattern_gen
* pclk_lvds{0, 1}_pattern_gen
* pclk_pcs0/1
* pclk_pcs{0,1}_ada
* pclk_mipitxphy0/1
* pclk_pwm_tx
* pclk_dft2apb
* pclk_lbist_ada_tx
*/
case RKX120_CPS_BUSCLK_TX_PRE:
pRate = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0);
freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
break;
/* gpio: 24M */
case RKX120_CPS_DCLK_TX_GPIO0:
case RKX120_CPS_DCLK_TX_GPIO1:
/* efuse: 24M */
case RKX120_CPS_CLK_TX_EFUSE:
/* pcs_ada: 24M */
case RKX120_CPS_CLK_PCS0_ADA:
case RKX120_CPS_CLK_PCS1_ADA:
/* capture_pwm: 24M */
case RKX120_CPS_CLK_CAPTURE_PWM_TX:
return OSC_24M;
case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
return _MHZ(200);
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return 0;
}
if (clkDiv) {
freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
}
return freq;
}
static HAL_Status RKX12x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
{
uint32_t clkMux = CLK_GET_MUX(clockName);
uint32_t clkDiv = CLK_GET_DIV(clockName);
uint32_t mux = 0, div = 1;
uint32_t pRate = 0;
uint32_t maxDiv;
uint32_t pll;
uint8_t overMax;
HAL_Status ret = HAL_OK;
switch (clockName) {
case RKX120_CPS_PLL_TXPLL:
ret = HAL_CRU_SetPllFreq(hw, &TXPLL_SETUP, rate);
hw->pllRate[RKX120_TXPLL] = rate;
CRU_MSG("%s: TXPLL set rate: %d\n", hw->name, rate);
return ret;
case RKX120_CPS_PLL_CPLL:
ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
hw->pllRate[RKX120_CPLL] = rate;
CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
return ret;
/* link(dclk): Allowed to change PLL rate if need ! */
case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
/* i2s */
case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
if (DIV_NO_REM(hw->pllRate[RKX120_TXPLL], rate, maxDiv)) {
mux = 0;
pRate = hw->pllRate[RKX120_TXPLL];
} else if (DIV_NO_REM(hw->pllRate[RKX120_CPLL], rate, maxDiv)) {
mux = 1;
pRate = hw->pllRate[RKX120_CPLL];
} else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
mux = 2;
pRate = OSC_24M;
} else {
if (clockName == RKX120_CPS_CLK_I2S_SRC_RKLINK_RX) {
pll = RKX120_CPS_PLL_CPLL;
if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_122888_BEST_PRATE;
} else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
pRate = I2S_112896_BEST_PRATE;
}
} else {
pll = RKX120_CPS_PLL_TXPLL;
}
/* PLL change closest new rate <= 1200M if need */
if (!pRate) {
pRate = (_MHZ(1200) / rate) * rate;
}
ret = RKX12x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
if (ret != HAL_OK) {
return ret;
}
/* if success, continue to set divider */
}
break;
/* lvds */
case RKX121_CPS_CLK_LVDS_C_LVDS_TX:
/* csi */
case RKX120_CPS_CLK_TXESC_CSITX0:
case RKX120_CPS_CLK_TXESC_CSITX1:
/* pwm */
case RKX120_CPS_CLK_PWM_TX:
case RKX120_CPS_PCLKOUT_DVPTX:
mux = HAL_CRU_RoundFreqGetMux3(hw, rate,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], OSC_24M, &pRate);
break;
/* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI0:
return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, rate);
/* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
case RKX120_CPS_ICLK_C_CSI1:
return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, rate);
/* lvds */
case RKX121_CPS_CLK_LVDS0:
case RKX121_CPS_CLK_LVDS1:
if (rate == _MHZ(150)) {
return HAL_CRU_ClkSetMux(hw, clkMux, 0); /* input clock: clk_lvds1_cm */
} else {
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* pre-bus */
case RKX120_CPS_BUSCLK_TX_PRE0:
mux = HAL_CRU_RoundFreqGetMux2(hw, rate,
hw->pllRate[RKX120_TXPLL],
hw->pllRate[RKX120_CPLL], &pRate);
break;
case RKX120_CPS_BUSCLK_TX_PRE:
if (rate == OSC_24M) {
return HAL_CRU_ClkSetMux(hw, clkMux, 0);
} else {
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0, rate);
return HAL_CRU_ClkSetMux(hw, clkMux, 1);
}
break;
/* gpio: 24M */
case RKX120_CPS_DCLK_TX_GPIO0:
case RKX120_CPS_DCLK_TX_GPIO1:
/* efuse: 24M */
case RKX120_CPS_CLK_TX_EFUSE:
/* pcs_ada: 24M */
case RKX120_CPS_CLK_PCS0_ADA:
case RKX120_CPS_CLK_PCS1_ADA:
/* capture_pwm: 24M */
case RKX120_CPS_CLK_CAPTURE_PWM_TX:
return rate == OSC_24M ? 0 : HAL_INVAL;
case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
if (rate != _MHZ(200)) {
return HAL_INVAL;
}
HAL_CRU_ClkSetMux(hw, clkMux, 0);
return HAL_OK;
default:
CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
return HAL_INVAL;
}
if (!clkMux && !clkDiv) {
return HAL_INVAL;
}
if (pRate) {
div = HAL_DIV_ROUND_UP(pRate, rate);
}
if (clkDiv) {
overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
if (overMax) {
CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
__func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
div = CLK_DIV_GET_MAXDIV(clkDiv);
}
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
}
if (clkMux) {
HAL_CRU_ClkSetMux(hw, clkMux, mux);
}
return HAL_OK;
}
#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
static void RKX12x_HAL_CRU_Init_SSCG(struct hwclk *hw)
{
uint8_t down_spread = 1; /* 0: center spread */
uint8_t amplitude = 8; /* range: 0x00 - 0x1f */
#if RKX120_SSCG_CPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
#endif
#if RKX120_SSCG_TXPLL_EN
/* down-spread, 0.8%, 37.5khz */
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
CRU_MSG("%s: TXPLL enable SSCG\n", hw->name);
#endif
}
#endif
static HAL_Status RKX12x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
uint32_t muxValue, uint32_t divValue)
{
uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
/* gpio0_b7: iomux to clk_testout */
HAL_CRU_Write(hw, RKX120_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
/* Enable clock */
HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
/* Mux, div */
HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
return HAL_OK;
}
static HAL_Status RKX12x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
{
hw->cru_base = 0x01000000;
hw->sel_con0 = hw->cru_base + 0x100;
hw->gate_con0 = hw->cru_base + 0x300;
hw->softrst_con0 = hw->cru_base + 0x400;
hw->gbl_srst_fst = 0x0614;
hw->flags = 0;
hw->num_gate = 16 * 12;
hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
if (!hw->gate) {
return HAL_NOMEM;
}
strcat(hw->name, "<CRU.121@");
strcat(hw->name, xfer->name);
strcat(hw->name, ">");
/* Don't change order */
#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
RKX12x_HAL_CRU_Init_SSCG(hw);
#endif
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_CPLL, _MHZ(1200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_TXPLL, _MHZ(1188));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE, _MHZ(100));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX, _MHZ(200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PMA2PCS2LINK_CM, _MHZ(200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, _MHZ(400));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PWM_TX, _MHZ(24));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX0, _MHZ(20));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX1, _MHZ(20));
/* Must be the same rate as RKX110_CPS_DCLK_RX_PRE when camera mode */
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, _MHZ(200));
RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, _MHZ(200));
HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
#if RKX120_TESTOUT_MUX >= 0
/* clk_testout support max 150M output, so set div=10 by default if not 24M */
RKX12x_HAL_CRU_InitTestout(hw, RKX120_CPS_TEST_CLKOUT, RKX120_TESTOUT_MUX,
RKX120_TESTOUT_MUX == 0 ? 1 : 10);
#endif
return HAL_OK;
}
PNAME(mux_24m_p) = { "xin24m" };
PNAME(mux_txpll_cpll_p) = { "txpll", "cpll" };
PNAME(mux_txpll_cpll_24m_p) = { "txpll", "cpll", "xin24m" };
PNAME(mux_24m_txpre0_p) = { "xin24m", "busclk_tx_pre0" };
#define CAL_FREQ_REG 0x01000f00
#define CAL_FREQ_EN_REG 0x01000f04
static uint32_t RKX12x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
{
uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
uint32_t freq, mhz;
uint8_t div = 10;
HAL_CRU_ClkSetDiv(hw, clkDiv, div);
HAL_CRU_ClkSetMux(hw, clkMux, clk);
HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1); /* NOTE: 1: enable, 0: disable */
HAL_SleepMs(2);
freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 0x0);
/* Fix accuracy */
if ((freq % 10) == 0x9) {
freq++;
}
freq *= (1000 * div);
/* If no external input, freq is close to 24M */
mhz = freq / 1000000;
if ((clk != 0) && (mhz == 23 || mhz == 24)) {
freq = 0;
}
return freq;
}
static struct clkTable rkx12x_clkTable[] = {
/* internal */
CLK_DECLARE_INT("txpll", RKX120_CPS_PLL_TXPLL, mux_24m_p),
CLK_DECLARE_INT("cpll", RKX120_CPS_PLL_CPLL, mux_24m_p),
CLK_DECLARE_INT("e0_clk_rklink_rx_pre", RKX120_CPS_E0_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("e1_clk_rklink_rx_pre", RKX120_CPS_E1_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("iclk_c_csi0", RKX120_CPS_ICLK_C_CSI0, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("iclk_c_csi1", RKX120_CPS_ICLK_C_CSI1, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_txesc_csitx0", RKX120_CPS_CLK_TXESC_CSITX0, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_txesc_csitx1", RKX120_CPS_CLK_TXESC_CSITX1, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_i2s_src_rklink_rx", RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("clk_pwm_tx", RKX120_CPS_CLK_PWM_TX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("pclkout_dvptx", RKX120_CPS_PCLKOUT_DVPTX, mux_txpll_cpll_24m_p),
CLK_DECLARE_INT("busclk_tx_pre0", RKX120_CPS_BUSCLK_TX_PRE0, mux_txpll_cpll_p),
CLK_DECLARE_INT("busclk_tx_pre", RKX120_CPS_BUSCLK_TX_PRE, mux_24m_txpre0_p),
CLK_DECLARE_INT("dclk_tx_gpio0", RKX120_CPS_DCLK_TX_GPIO0, mux_24m_p),
CLK_DECLARE_INT("dclk_tx_gpio1", RKX120_CPS_DCLK_TX_GPIO1, mux_24m_p),
CLK_DECLARE_INT("clk_tx_efuse", RKX120_CPS_CLK_TX_EFUSE, mux_24m_p),
CLK_DECLARE_INT("clk_pcs0_ada", RKX120_CPS_CLK_PCS0_ADA, mux_24m_p),
CLK_DECLARE_INT("clk_pcs1_ada", RKX120_CPS_CLK_PCS1_ADA, mux_24m_p),
CLK_DECLARE_INT("clk_capture_pwm_tx", RKX120_CPS_CLK_CAPTURE_PWM_TX, mux_24m_p),
/* external */
CLK_DECLARE_EXT("xin24m", 0, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_csitx0", 3, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_csitx1", 5, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_dsitx", 7, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_rxesc_dsitx", 8, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_pmarx0_pixel", 11, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_pmarx1_pixel", 12, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy0_lvds", 13, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy0_pixel", 14, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy1_lvds", 15, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_mipitxphy1_pixel", 16, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txbytehs_dsitx_csitx0", 23, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_c_dvp_src", 24, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_d_dsi_src", 25, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_lvds0_src", 26, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_lvds1_src", 27, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("dclk_rgb_src", 28, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_2x_pma2pcs0", 29, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_2x_pma2pcs1", 30, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT("clk_txesc_mipitxphy0", 31, RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("dclk_d_ds", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("dclk_d_dsi_pattern_gen", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds0", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds0_pattern_gen", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds1", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_lvds1_pattern_gen", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 10, "clk_link_pcs1", RKX12x_HAL_CRU_ClkGetExtFreq),
{ /* sentinel */ },
};
struct clkOps rkx121_clk_ops =
{
.clkTable = rkx12x_clkTable,
.clkInit = RKX12x_HAL_CRU_Init,
.clkGetFreq = RKX12x_HAL_CRU_ClkGetFreq,
.clkSetFreq = RKX12x_HAL_CRU_ClkSetFreq,
.clkInitTestout = RKX12x_HAL_CRU_InitTestout,
};

View File

@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _CRU_RKX121_H
#include "cru_rkx120.h"
// TXCRU_SOFTRST_CON06(Offset:0x418)
#define RKX121_SRST_DRESETN_C_LVDS_TX 0x0000006A
#define RKX121_SRST_LVDS_RESETN_C_LVDS_TX 0x0000006B
#define RKX121_SRST_PRESETN_C_LVDS_TX 0x0000006C
// TXCRU_SOFTRST_CON11(Offset:0x42C)
#define RKX121_SRST_PRESETN_LBIST_ADA_TX 0x000000B1
// TXCRU_GATE_CON06(Offset:0x318)
#define RKX121_DCLK_C_LVDS_TX_GATE 0x0000006A
#define RKX121_CLK_LVDS_C_LVDS_TX_GATE 0x0000006B
#define RKX121_PCLK_C_LVDS_TX_GATE 0x0000006C
// TXCRU_GATE_CON11(Offset:0x32C)
#define RKX121_PCLK_LBIST_ADA_TX_GATE 0x000000B1
// TXCRU_CLKSEL_CON08(Offset:0x120)
#define RKX121_CLK_LVDS1_SEL 0x01010008
#define RKX121_CLK_LVDS1_SEL_CLK_LVDS1_CM 0U
#define RKX121_CLK_LVDS1_SEL_CLK_LVDS_C_LVDS_TX 1U
#define RKX121_CLK_LVDS0_SEL 0x01020008
#define RKX121_CLK_LVDS0_SEL_CLK_LVDS0_CM 0U
#define RKX121_CLK_LVDS0_SEL_CLK_LVDS_C_LVDS_TX 1U
// TXCRU_CLKSEL_CON09(Offset:0x124)
#define RKX121_CLK_LVDS_C_LVDS_TX_DIV 0x08000009
#define RKX121_CLK_LVDS_C_LVDS_TX_SEL 0x020E0009
#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_CLK_TXPLL_MUX 0U
#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_CLK_CPLL_MUX 1U
#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_XIN_OSC0_FUNC 2U
/* COMPOSITE */
#define RKX121_CPS_CLK_LVDS_C_LVDS_TX COMPOSITE_CLK(RKX121_CLK_LVDS_C_LVDS_TX_SEL, RKX121_CLK_LVDS_C_LVDS_TX_DIV)
#define RKX121_CPS_CLK_LVDS0 COMPOSITE_CLK(RKX121_CLK_LVDS0_SEL, 0)
#define RKX121_CPS_CLK_LVDS1 COMPOSITE_CLK(RKX121_CLK_LVDS1_SEL, 0)
#endif

View File

@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Steven Liu <steven.liu@rock-chips.com>
*/
#ifndef _HAL_DEF_H_
#define _HAL_DEF_H_
#include "hal_os_def.h"
#define HAL_LOGLEVEL 3
#define __hal_print(level, fmt, ...) \
({ \
level < HAL_LOGLEVEL ? HAL_SYSLOG("[HAL] " fmt, ##__VA_ARGS__) : 0; \
})
#define HAL_ERR(fmt, ...) __hal_print(0, "ERROR: " fmt, ##__VA_ARGS__)
#define HAL_WARN(fmt, ...) __hal_print(1, "WARN: " fmt, ##__VA_ARGS__)
#define HAL_MSG(fmt, ...) __hal_print(2, fmt, ##__VA_ARGS__)
#define HAL_DBG(fmt, ...) __hal_print(3, fmt, ##__VA_ARGS__)
#define HAL_NULL ((void *)0)
#define HAL_BIT(nr) (1UL << (nr))
/***************************** Structure Definition **************************/
/** HAL boolean type definition */
typedef enum {
HAL_FALSE = 0x00U,
HAL_TRUE = 0x01U
} HAL_Check;
/** HAL error code definition */
typedef enum {
HAL_OK = 0x00U,
HAL_ERROR = (-1),
HAL_NOMEM = (-12),
HAL_BUSY = (-16),
HAL_NODEV = (-19),
HAL_INVAL = (-22),
HAL_NOSYS = (-38),
HAL_TIMEOUT = (-110)
} HAL_Status;
/** HAL functional status definition */
typedef enum {
HAL_DISABLE = 0x00U,
HAL_ENABLE = 0x01U
} HAL_FuncStatus;
/** HAL lock structures definition */
typedef enum {
HAL_UNLOCKED = 0x00U,
HAL_LOCKED = 0x01U
} HAL_LockStatus;
#endif /* _HAL_DEF_H_ */

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Joseph Chen <chenjh@rock-chips.com>
*/
#ifndef _HAL_OS_DEF_H_
#define _HAL_OS_DEF_H_
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#define HAL_BITS_PER_LONG BITS_PER_LONG
#define HAL_SYSLOG pr_info
#define HAL_DelayUs udelay
#define HAL_SleepMs msleep
#define HAL_ARRAY_SIZE ARRAY_SIZE
#define HAL_DEFINE_MUTEX DEFINE_MUTEX
#define HAL_Mutex struct mutex
#define HAL_MutexInit mutex_init
#define HAL_MutexLock mutex_lock
#define HAL_MutexUnlock mutex_unlock
#define HAL_KCALLOC(n, size) kcalloc(n, size, GFP_KERNEL)
typedef int (HAL_RegRead_t)(struct i2c_client *client, uint32_t addr, uint32_t *value);
typedef int (HAL_RegWrite_t)(struct i2c_client *client, uint32_t addr, uint32_t value);
#endif /* _HAL_OS_DEF_H_ */

View File

@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Steven Liu <steven.liu@rock-chips.com>
*/
#ifndef _PINCTRL_API_H_
#define _PINCTRL_API_H_
#include "hal_def.h"
#include "hal_os_def.h"
#include "pinctrl_rkx110_x120.h"
static inline int hwpin_set(struct xferpin xfer)
{
struct hwpin hw;
int ret;
if (!xfer.read || !xfer.write || !xfer.client || !xfer.name[0]) {
return HAL_ERROR;
}
if (xfer.type == PIN_UNDEF || xfer.type >= PIN_MAX) {
return HAL_INVAL;
}
memset(&hw, 0, sizeof(hw));
hw.type = xfer.type;
hw.xfer = xfer;
hw.bank = xfer.bank;
hw.mpins = xfer.mpins;
hw.param = xfer.param;
ret = HAL_PINCTRL_SetParam(&hw, hw.mpins, hw.param);
return ret;
}
static inline int hwpin_init(void)
{
return HAL_PINCTRL_Init();
}
#endif

View File

@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Steven Liu <steven.liu@rock-chips.com>
*/
#include "pinctrl_rkx110_x120.h"
/********************* Private MACRO Definition ******************************/
#define _PINCTRL_GENMASK(w) ((1U << (w)) - 1U)
#define _PINCTRL_OFFSET(gp, w) ((gp) * (w))
#define _PINCTRL_GENVAL(gp, v, w) ((_PINCTRL_GENMASK(w) << (_PINCTRL_OFFSET(gp, w) + 16)) \
| (((v) & _PINCTRL_GENMASK(w)) << _PINCTRL_OFFSET(gp, w)))
#define RKX110_GRF_BASE 0x00010000U
#define RKX120_GRF_BASE 0x01010000U
#define RKX110_GRF_REG(x) ((x) + RKX110_GRF_BASE)
#define RKX120_GRF_REG(x) ((x) + RKX120_GRF_BASE)
/************************** SER_GRF Register Define ***************************/
#define RKX110_GRF_GPIO0A_IOMUX_L RKX110_GRF_REG(0x0000)
#define RKX110_GRF_GPIO0A_IOMUX_H RKX110_GRF_REG(0x0004)
#define RKX110_GRF_GPIO0B_IOMUX_L RKX110_GRF_REG(0x0008)
#define RKX110_GRF_GPIO0B_IOMUX_H RKX110_GRF_REG(0x000c)
#define RKX110_GRF_GPIO0C_IOMUX_L RKX110_GRF_REG(0x0010)
#define RKX110_GRF_GPIO0C_IOMUX_H RKX110_GRF_REG(0x0014)
#define RKX110_GRF_GPIO0A_P RKX110_GRF_REG(0x0020)
#define RKX110_GRF_GPIO0B_P RKX110_GRF_REG(0x0024)
#define RKX110_GRF_GPIO0C_P RKX110_GRF_REG(0x0028)
#define RKX110_GRF_GPIO1A_IOMUX RKX110_GRF_REG(0x0080)
#define RKX110_GRF_GPIO1B_IOMUX RKX110_GRF_REG(0x0084)
#define RKX110_GRF_GPIO1C_IOMUX RKX110_GRF_REG(0x0088)
#define RKX110_GRF_GPIO1A_P RKX110_GRF_REG(0x0090)
#define RKX110_GRF_GPIO1B_P RKX110_GRF_REG(0x0094)
#define RKX110_GRF_GPIO1C_P RKX110_GRF_REG(0x0098)
#define RKX110_GRF_GPIO1A_SMT RKX110_GRF_REG(0x00A0)
#define RKX110_GRF_GPIO1B_SMT RKX110_GRF_REG(0x00A4)
#define RKX110_GRF_GPIO1C_SMT RKX110_GRF_REG(0x00A8)
#define RKX110_GRF_GPIO1A_E RKX110_GRF_REG(0x00B0)
#define RKX110_GRF_GPIO1B_E RKX110_GRF_REG(0x00B4)
#define RKX110_GRF_GPIO1C_E RKX110_GRF_REG(0x00B8)
#define RKX110_GRF_GPIO1A_IE RKX110_GRF_REG(0x00C0)
#define RKX110_GRF_GPIO1B_IE RKX110_GRF_REG(0x00C4)
#define RKX110_GRF_GPIO1C_IE RKX110_GRF_REG(0x00C8)
/************************** DES_GRF Register Define ***************************/
#define RKX120_GRF_GPIO0A_IOMUX_L RKX120_GRF_REG(0x0000)
#define RKX120_GRF_GPIO0A_IOMUX_H RKX120_GRF_REG(0x0004)
#define RKX120_GRF_GPIO0B_IOMUX_L RKX120_GRF_REG(0x0008)
#define RKX120_GRF_GPIO0B_IOMUX_H RKX120_GRF_REG(0x000C)
#define RKX120_GRF_GPIO0C_IOMUX_L RKX120_GRF_REG(0x0010)
#define RKX120_GRF_GPIO0C_IOMUX_H RKX120_GRF_REG(0x0014)
#define RKX120_GRF_GPIO0A_P RKX120_GRF_REG(0x0020)
#define RKX120_GRF_GPIO0B_P RKX120_GRF_REG(0x0024)
#define RKX120_GRF_GPIO0C_P RKX120_GRF_REG(0x0028)
#define RKX120_GRF_GPIO1A_IOMUX RKX120_GRF_REG(0x0080)
#define RKX120_GRF_GPIO1B_IOMUX RKX120_GRF_REG(0x0084)
#define RKX120_GRF_GPIO1C_IOMUX RKX120_GRF_REG(0x0088)
#define RKX120_GRF_GPIO1A_P RKX120_GRF_REG(0x0090)
#define RKX120_GRF_GPIO1B_P RKX120_GRF_REG(0x0094)
#define RKX120_GRF_GPIO1C_P RKX120_GRF_REG(0x0098)
#define RKX120_GRF_GPIO1A_SMT RKX120_GRF_REG(0x00A0)
#define RKX120_GRF_GPIO1B_SMT RKX120_GRF_REG(0x00A4)
#define RKX120_GRF_GPIO1C_SMT RKX120_GRF_REG(0x00A8)
#define RKX120_GRF_GPIO1A_E RKX120_GRF_REG(0x00B0)
#define RKX120_GRF_GPIO1B_E RKX120_GRF_REG(0x00B4)
#define RKX120_GRF_GPIO1C_E RKX120_GRF_REG(0x00B8)
#define RKX120_GRF_GPIO1A_IE RKX120_GRF_REG(0x00C0)
#define RKX120_GRF_GPIO1B_IE RKX120_GRF_REG(0x00C4)
#define RKX120_GRF_GPIO1C_IE RKX120_GRF_REG(0x00C8)
#define IOMUX_3_BIT_PER_PIN (3)
#define IOMUX_PER_REG (8)
#define RKX110_IOMUX_L(__B, __G) (RKX110_GRF_GPIO##__B##__G##_IOMUX_L)
#define RKX110_IOMUX_H(__B, __G) (RKX110_GRF_GPIO##__B##__G##_IOMUX_H)
#define RKX110_SET_IOMUX_L(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_IOMUX_L(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define RKX110_SET_IOMUX_H(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_IOMUX_H(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_IOMUX_L(HW, B, G, BP, V) \
RKX110_SET_IOMUX_L(HW, B, G, BP % IOMUX_PER_REG, V, IOMUX_3_BIT_PER_PIN)
#define HAL_RKX110_SET_IOMUX_H(HW, B, G, BP, V) \
RKX110_SET_IOMUX_H(HW, B, G, (BP % IOMUX_PER_REG - 5), V, IOMUX_3_BIT_PER_PIN)
#define RKX120_IOMUX_L(__B, __G) (RKX120_GRF_GPIO##__B##__G##_IOMUX_L)
#define RKX120_IOMUX_H(__B, __G) (RKX120_GRF_GPIO##__B##__G##_IOMUX_H)
#define RKX120_SET_IOMUX_L(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_IOMUX_L(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define RKX120_SET_IOMUX_H(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_IOMUX_H(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_IOMUX_L(HW, B, G, BP, V) \
RKX120_SET_IOMUX_L(HW, B, G, BP % IOMUX_PER_REG, V, IOMUX_3_BIT_PER_PIN)
#define HAL_RKX120_SET_IOMUX_H(HW, B, G, BP, V) \
RKX120_SET_IOMUX_H(HW, B, G, (BP % IOMUX_PER_REG - 5), V, IOMUX_3_BIT_PER_PIN)
#define IOMUX_2_BIT_PER_PIN (2)
#define IOMUX_8_PIN_PER_REG (8)
#define RKX110_IOMUX_0(__B, __G) (RKX110_GRF_GPIO##__B##__G##_IOMUX)
#define RKX110_SET_IOMUX_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_IOMUX_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_IOMUX_0(HW, B, G, BP, V) \
RKX110_SET_IOMUX_0(HW, B, G, BP % IOMUX_8_PIN_PER_REG, V, IOMUX_2_BIT_PER_PIN)
#define RKX120_IOMUX_0(__B, __G) (RKX120_GRF_GPIO##__B##__G##_IOMUX)
#define RKX120_SET_IOMUX_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_IOMUX_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_IOMUX_0(HW, B, G, BP, V) \
RKX120_SET_IOMUX_0(HW, B, G, BP % IOMUX_8_PIN_PER_REG, V, IOMUX_2_BIT_PER_PIN)
#define PULL_2_BIT_PER_PIN (2)
#define PULL_8_PIN_PER_REG (8)
#define RKX110_PULL_0(__B, __G) (RKX110_GRF_GPIO##__B##__G##_P)
#define RKX110_SET_PULL_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_PULL_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_PULL_0(HW, B, G, BP, V) \
RKX110_SET_PULL_0(HW, B, G, BP % PULL_8_PIN_PER_REG, V, PULL_2_BIT_PER_PIN)
#define RKX120_PULL_0(__B, __G) (RKX120_GRF_GPIO##__B##__G##_P)
#define RKX120_SET_PULL_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_PULL_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_PULL_0(HW, B, G, BP, V) \
RKX120_SET_PULL_0(HW, B, G, BP % PULL_8_PIN_PER_REG, V, PULL_2_BIT_PER_PIN)
#define PULLEN_1_BIT_PER_PIN (1)
#define PULLEN_8_PIN_PER_REG (8)
#define RKX110_PULLEN_0(__B, __G) (RKX110_GRF_GPIO##__B##__G##_P)
#define RKX110_SET_PULLEN_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_PULLEN_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_PULLEN_0(HW, B, G, BP, V) \
RKX110_SET_PULLEN_0(HW, B, G, BP % PULLEN_8_PIN_PER_REG, V, PULLEN_1_BIT_PER_PIN)
#define RKX120_PULLEN_0(__B, __G) (RKX120_GRF_GPIO##__B##__G##_P)
#define RKX120_SET_PULLEN_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_PULLEN_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_PULLEN_0(HW, B, G, BP, V) \
RKX120_SET_PULLEN_0(HW, B, G, BP % PULLEN_8_PIN_PER_REG, V, PULLEN_1_BIT_PER_PIN)
#define DRV_2_BIT_PER_PIN (2)
#define DRV_8_PIN_PER_REG (8)
#define RKX110_DRV_0(__B, __G) (RKX110_GRF_GPIO##__B##__G##_E)
#define RKX110_SET_DRV_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_DRV_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_DRV_0(HW, B, G, BP, V) \
RKX110_SET_DRV_0(HW, B, G, BP % DRV_8_PIN_PER_REG, V, DRV_2_BIT_PER_PIN)
#define RKX120_DRV_0(__B, __G) (RKX120_GRF_GPIO##__B##__G##_E)
#define RKX120_SET_DRV_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_DRV_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_DRV_0(HW, B, G, BP, V) \
RKX120_SET_DRV_0(HW, B, G, BP % DRV_8_PIN_PER_REG, V, DRV_2_BIT_PER_PIN)
#define SMT_1_BIT_PER_PIN (1)
#define SMT_8_PIN_PER_REG (8)
#define RKX110_SMT_0(__B, __G) (RKX110_GRF_GPIO##__B##__G##_SMT)
#define RKX110_SET_SMT_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX110_SMT_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX110_SET_SMT_0(HW, B, G, BP, V) \
RKX110_SET_SMT_0(HW, B, G, BP % SMT_8_PIN_PER_REG, V, SMT_1_BIT_PER_PIN)
#define RKX120_SMT_0(__B, __G) (RKX120_GRF_GPIO##__B##__G##_SMT)
#define RKX120_SET_SMT_0(_HW, _B, _G, _GP, _V, _W) \
{ \
HAL_PINCTRL_Write(_HW, RKX120_SMT_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W)); \
}
#define HAL_RKX120_SET_SMT_0(HW, B, G, BP, V) \
RKX120_SET_SMT_0(HW, B, G, BP % SMT_8_PIN_PER_REG, V, SMT_1_BIT_PER_PIN)
/********************* Private Function Definition ***************************/
static HAL_Status RKX110_PINCTRL_SetIOMUX(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 0:
if (pin < 5) {
HAL_RKX110_SET_IOMUX_L(hw, 0, A, pin, val);
} else if (pin < 8) {
HAL_RKX110_SET_IOMUX_H(hw, 0, A, pin, val);
} else if (pin < 13) {
HAL_RKX110_SET_IOMUX_L(hw, 0, B, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_IOMUX_H(hw, 0, B, pin, val);
} else if (pin < 21) {
HAL_RKX110_SET_IOMUX_L(hw, 0, C, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_IOMUX_H(hw, 0, C, pin, val);
} else {
HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
}
break;
case 1:
if (pin < 8) {
HAL_RKX110_SET_IOMUX_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_IOMUX_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_IOMUX_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: iomux unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX110_PINCTRL_SetPULL(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 1:
if (pin < 8) {
HAL_RKX110_SET_PULL_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_PULL_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_PULL_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: pull unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: pull unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX110_PINCTRL_SetPULLEN(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 0:
if (pin < 8) {
HAL_RKX110_SET_PULLEN_0(hw, 0, A, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_PULLEN_0(hw, 0, B, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_PULLEN_0(hw, 0, C, pin, val);
} else {
HAL_ERR("PINCTRL: pull enable unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: pull enable unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX110_PINCTRL_SetDRV(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 1:
if (pin < 8) {
HAL_RKX110_SET_DRV_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_DRV_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_DRV_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: drive strengh unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: drive strengh gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX110_PINCTRL_SetSMT(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 1:
if (pin < 8) {
HAL_RKX110_SET_SMT_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX110_SET_SMT_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX110_SET_SMT_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: smt gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: smt gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX120_PINCTRL_SetIOMUX(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 5:
if (pin < 5) {
HAL_RKX120_SET_IOMUX_L(hw, 0, A, pin, val);
} else if (pin < 8) {
HAL_RKX120_SET_IOMUX_H(hw, 0, A, pin, val);
} else if (pin < 13) {
HAL_RKX120_SET_IOMUX_L(hw, 0, B, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_IOMUX_H(hw, 0, B, pin, val);
} else if (pin < 21) {
HAL_RKX120_SET_IOMUX_L(hw, 0, C, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_IOMUX_H(hw, 0, C, pin, val);
} else {
HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
}
break;
case 6:
if (pin < 8) {
HAL_RKX120_SET_IOMUX_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_IOMUX_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_IOMUX_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: iomux unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX120_PINCTRL_SetPULL(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 6:
if (pin < 8) {
HAL_RKX120_SET_PULL_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_PULL_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_PULL_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: pull unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: pull unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX120_PINCTRL_SetPULLEN(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 5:
if (pin < 8) {
HAL_RKX120_SET_PULLEN_0(hw, 0, A, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_PULLEN_0(hw, 0, B, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_PULLEN_0(hw, 0, C, pin, val);
} else {
HAL_ERR("PINCTRL: pull enable unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: pull enable unknown gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX120_PINCTRL_SetDRV(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 6:
if (pin < 8) {
HAL_RKX120_SET_DRV_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_DRV_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_DRV_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: drive strengh unknown gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: drive strengh gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status RKX120_PINCTRL_SetSMT(struct hwpin *hw, uint8_t pin, uint32_t val)
{
switch (hw->bank) {
case 6:
if (pin < 8) {
HAL_RKX120_SET_SMT_0(hw, 1, A, pin, val);
} else if (pin < 16) {
HAL_RKX120_SET_SMT_0(hw, 1, B, pin, val);
} else if (pin < 24) {
HAL_RKX120_SET_SMT_0(hw, 1, C, pin, val);
} else {
HAL_ERR("PINCTRL: smt gpio pin-%d\n", pin);
}
break;
default:
HAL_ERR("PINCTRL: smt gpio bank-%d\n", hw->bank);
break;
}
return HAL_OK;
}
static HAL_Status PINCTRL_SetPinParam(struct hwpin *hw, uint8_t pin, uint32_t param)
{
HAL_Status rc = HAL_OK;
uint8_t val;
if (hw->type == PIN_RKX110) {
if (param & RK_SERDES_FLAG_MUX) {
val = (param & RK_SERDES_MASK_MUX) >> RK_SERDES_SHIFT_MUX;
rc |= RKX110_PINCTRL_SetIOMUX(hw, pin, val);
}
if (param & RK_SERDES_FLAG_PUL) {
val = (param & RK_SERDES_MASK_PUL) >> RK_SERDES_SHIFT_PUL;
rc |= RKX110_PINCTRL_SetPULL(hw, pin, val);
}
if (param & RK_SERDES_FLAG_PEN) {
val = (param & RK_SERDES_MASK_PEN) >> RK_SERDES_SHIFT_PEN;
rc |= RKX110_PINCTRL_SetPULLEN(hw, pin, val);
}
if (param & RK_SERDES_FLAG_DRV) {
val = (param & RK_SERDES_MASK_DRV) >> RK_SERDES_SHIFT_DRV;
rc |= RKX110_PINCTRL_SetDRV(hw, pin, val);
}
if (param & RK_SERDES_FLAG_SMT) {
val = (param & RK_SERDES_MASK_SMT) >> RK_SERDES_SHIFT_SMT;
rc |= RKX110_PINCTRL_SetSMT(hw, pin, val);
}
} else if (hw->type == PIN_RKX120) {
if (param & RK_SERDES_FLAG_MUX) {
val = (param & RK_SERDES_MASK_MUX) >> RK_SERDES_SHIFT_MUX;
rc |= RKX120_PINCTRL_SetIOMUX(hw, pin, val);
}
if (param & RK_SERDES_FLAG_PUL) {
val = (param & RK_SERDES_MASK_PUL) >> RK_SERDES_SHIFT_PUL;
rc |= RKX120_PINCTRL_SetPULL(hw, pin, val);
}
if (param & RK_SERDES_FLAG_PEN) {
val = (param & RK_SERDES_MASK_PEN) >> RK_SERDES_SHIFT_PEN;
rc |= RKX120_PINCTRL_SetPULLEN(hw, pin, val);
}
if (param & RK_SERDES_FLAG_DRV) {
val = (param & RK_SERDES_MASK_DRV) >> RK_SERDES_SHIFT_DRV;
rc |= RKX120_PINCTRL_SetDRV(hw, pin, val);
}
if (param & RK_SERDES_FLAG_SMT) {
val = (param & RK_SERDES_MASK_SMT) >> RK_SERDES_SHIFT_SMT;
rc |= RKX120_PINCTRL_SetSMT(hw, pin, val);
}
} else {
HAL_ERR("PINCTRL: error pin type %d\n", hw->type);
}
return rc;
}
/********************* Public Function Definition ***************************/
uint32_t HAL_PINCTRL_Read(struct hwpin *hw, uint32_t reg)
{
uint32_t val;
int ret;
ret = hw->xfer.read(hw->xfer.client, reg, &val);
if (ret) {
HAL_ERR("%s: read reg=0x%08x failed, ret=%d\n", hw->name, reg, ret);
}
HAL_DBG("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
return val;
}
uint32_t HAL_PINCTRL_Write(struct hwpin *hw, uint32_t reg, uint32_t val)
{
int ret;
HAL_DBG("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
ret = hw->xfer.write(hw->xfer.client, reg, val);
if (ret) {
HAL_ERR("%s: write reg=0x%08x, val=0x%08x failed, ret=%d\n",
hw->name, reg, val, ret);
}
return ret;
}
HAL_Status HAL_PINCTRL_Init(void)
{
return HAL_OK;
}
HAL_Status HAL_PINCTRL_SetParam(struct hwpin *hw, uint32_t mPins, uint32_t param)
{
uint8_t pin;
HAL_Status rc;
if (!(param & (RK_SERDES_FLAG_MUX | RK_SERDES_FLAG_PUL | RK_SERDES_FLAG_PEN |
RK_SERDES_FLAG_DRV | RK_SERDES_FLAG_SMT))) {
HAL_ERR("PINCTRL: no parameter!\n");
return HAL_ERROR;
}
for (pin = 0; pin < 32; pin++) {
if (mPins & (1 << pin)) {
rc = PINCTRL_SetPinParam(hw, pin, param);
if (rc) {
return rc;
}
}
}
return HAL_OK;
}
HAL_Status HAL_PINCTRL_SetIOMUX(struct hwpin *hw, uint32_t mPins, uint32_t param)
{
return HAL_PINCTRL_SetParam(hw, mPins, param);
}

View File

@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Rockchip Electronics Co. Ltd.
*
* Author: Steven Liu <steven.liu@rock-chips.com>
*/
#ifndef _PINCTRL_CORE_H_
#define _PINCTRL_CORE_H_
#include <dt-bindings/mfd/rockchip-serdes.h>
#include "hal_def.h"
#include "hal_os_def.h"
typedef enum {
PIN_UNDEF,
PIN_RKX110,
PIN_RKX120,
PIN_ALL,
PIN_MAX,
} HAL_PinType;
struct hwpin;
struct xferpin {
HAL_PinType type;
char *name; /* slave addr is expected */
void *client;
uint32_t bank;
uint32_t mpins;
uint32_t param;
HAL_RegRead_t *read;
HAL_RegWrite_t *write;
};
struct hwpin {
char name[32];
HAL_PinType type;
uint32_t grf_base;
uint32_t bank;
uint32_t mpins;
uint32_t param;
struct xferpin xfer;
};
typedef enum {
GPIO0_A0 = 0,
GPIO0_A1,
GPIO0_A2,
GPIO0_A3,
GPIO0_A4,
GPIO0_A5,
GPIO0_A6,
GPIO0_A7,
GPIO0_B0 = 8,
GPIO0_B1,
GPIO0_B2,
GPIO0_B3,
GPIO0_B4,
GPIO0_B5,
GPIO0_B6,
GPIO0_B7,
GPIO0_C0 = 16,
GPIO0_C1,
GPIO0_C2,
GPIO0_C3,
GPIO0_C4,
GPIO0_C5,
GPIO0_C6,
GPIO0_C7,
GPIO0_D0 = 24,
GPIO0_D1,
GPIO0_D2,
GPIO0_D3,
GPIO0_D4,
GPIO0_D5,
GPIO0_D6,
GPIO0_D7,
GPIO1_A0 = 32,
GPIO1_A1,
GPIO1_A2,
GPIO1_A3,
GPIO1_A4,
GPIO1_A5,
GPIO1_A6,
GPIO1_A7,
GPIO1_B0 = 40,
GPIO1_B1,
GPIO1_B2,
GPIO1_B3,
GPIO1_B4,
GPIO1_B5,
GPIO1_B6,
GPIO1_B7,
GPIO1_C0 = 48,
GPIO1_C1,
GPIO1_C2,
GPIO1_C3,
GPIO1_C4,
GPIO1_C5,
GPIO1_C6,
GPIO1_C7,
GPIO1_D0 = 56,
GPIO1_D1,
GPIO1_D2,
GPIO1_D3,
GPIO1_D4,
GPIO1_D5,
GPIO1_D6,
GPIO1_D7,
GPIO_NUM_MAX
} ePINCTRL_PIN;
typedef enum {
PINCTRL_IOMUX_FUNC0,
PINCTRL_IOMUX_FUNC1,
PINCTRL_IOMUX_FUNC2,
PINCTRL_IOMUX_FUNC3,
PINCTRL_IOMUX_FUNC4,
PINCTRL_IOMUX_FUNC5,
PINCTRL_IOMUX_FUNC6,
PINCTRL_IOMUX_FUNC7,
} ePINCTRL_iomuxFunc;
typedef enum {
PINCTRL_PULL_NORMAL,
PINCTRL_PULL_UP,
PINCTRL_PULL_DOWN,
PINCTRL_PULL_KEEP
} ePINCTRL_pullMode;
/*
* Special pull configuration.
* Only enable and disable.
* The specific pull-up or pull-down can not be configured.
*/
typedef enum {
PINCTRL_PULL_DIS,
PINCTRL_PULL_EN
} ePINCTRL_pullEnable;
typedef enum {
PINCTRL_DRIVE_LEVEL0,
PINCTRL_DRIVE_LEVEL1,
PINCTRL_DRIVE_LEVEL2,
PINCTRL_DRIVE_LEVEL3,
PINCTRL_DRIVE_LEVEL4,
PINCTRL_DRIVE_LEVEL5,
PINCTRL_DRIVE_LEVEL6,
PINCTRL_DRIVE_LEVEL7
} ePINCTRL_driveLevel;
typedef enum {
PINCTRL_SCHMITT_DIS,
PINCTRL_SCHMITT_EN
} ePINCTRL_schmitt;
uint32_t HAL_PINCTRL_Read(struct hwpin *hw, uint32_t reg);
uint32_t HAL_PINCTRL_Write(struct hwpin *hw, uint32_t reg, uint32_t val);
HAL_Status HAL_PINCTRL_Init(void);
HAL_Status HAL_PINCTRL_SetParam(struct hwpin *hw, uint32_t mPins, uint32_t param);
HAL_Status HAL_PINCTRL_SetIOMUX(struct hwpin *hw, uint32_t mPins, uint32_t param);
#endif /* _PINCTRL_CORE_H_ */

View File

@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
*/
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include "rkx110_x120.h"
#define PATTERN_GEN_PATTERN_CTRL 0x0000
#define PATTERN_START_PCLK BIT(31)
#define PATTERN_START BIT(30)
#define PATTERN_RECTANGLE_H GENMASK(29, 16)
#define PATTERN_RECTANGLE_V GENMASK(13, 0)
#define PATTERN_GEN_PATERN_VH_CFG0 0x0004
#define PATTERN_HACT GENMASK(29, 16)
#define PATTERN_VACT GENMASK(13, 0)
#define PATTERN_GEN_PATERN_VH_CFG1 0x0008
#define PATTERN_VFP GENMASK(29, 20)
#define PATTERN_VBP GENMASK(19, 10)
#define PATTERN_VSA GENMASK(9, 0)
#define PATTERN_GEN_PATERN_VH_CFG2 0x000C
#define PATTERN_HFP GENMASK(27, 16)
#define PATTERN_HBP GENMASK(11, 0)
#define PATTERN_GEN_PATERN_VH_CFG3 0x0010
#define PATTERN_HSA GENMASK(11, 0)
#define PATTERN_GEN_VALUE0 0x0014
#define PATTERN_GEN_VALUE1 0x0018
static void pattern_gen_enable(struct pattern_gen *pattern_gen)
{
struct i2c_client *client = pattern_gen->chip->client;
struct rk_serdes *serdes = pattern_gen->chip->serdes;
const struct videomode *vm = serdes->vm;
serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
PATTERN_RECTANGLE_H | PATTERN_RECTANGLE_V,
FIELD_PREP(PATTERN_RECTANGLE_H, 128) |
FIELD_PREP(PATTERN_RECTANGLE_V, 128));
serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG0,
FIELD_PREP(PATTERN_HACT, vm->hactive) |
FIELD_PREP(PATTERN_VACT, vm->vactive));
serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG1,
FIELD_PREP(PATTERN_VFP, vm->vfront_porch) |
FIELD_PREP(PATTERN_VBP, vm->vback_porch) |
FIELD_PREP(PATTERN_VSA, vm->vsync_len));
serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG2,
FIELD_PREP(PATTERN_HFP, vm->hfront_porch) |
FIELD_PREP(PATTERN_HBP, vm->hback_porch));
serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG3,
FIELD_PREP(PATTERN_HSA, vm->hsync_len));
serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
PATTERN_START_PCLK,
FIELD_PREP(PATTERN_START_PCLK, 1));
serdes->i2c_write_reg(client, pattern_gen->link_src_reg,
BIT(pattern_gen->link_src_offset + 16) |
BIT(pattern_gen->link_src_offset));
}
static void pattern_gen_disable(struct pattern_gen *pattern_gen)
{
struct i2c_client *client = pattern_gen->chip->client;
struct rk_serdes *serdes = pattern_gen->chip->serdes;
serdes->i2c_write_reg(client, pattern_gen->link_src_reg,
BIT(pattern_gen->link_src_offset + 16));
serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
PATTERN_START_PCLK,
FIELD_PREP(PATTERN_START_PCLK, 0));
}
static ssize_t pattern_gen_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct pattern_gen *pattern_gen = m->private;
char buf[5];
if (len > sizeof(buf) - 1)
return -EINVAL;
if (copy_from_user(buf, ubuf, len))
return -EFAULT;
buf[len] = '\0';
if (sysfs_streq(buf, "on"))
pattern_gen_enable(pattern_gen);
else if (sysfs_streq(buf, "off"))
pattern_gen_disable(pattern_gen);
else
return -EINVAL;
return len;
}
static int pattern_gen_show(struct seq_file *m, void *data)
{
struct pattern_gen *pattern_gen = m->private;
struct i2c_client *client = pattern_gen->chip->client;
struct rk_serdes *serdes = pattern_gen->chip->serdes;
u32 reg = 0;
serdes->i2c_read_reg(client, pattern_gen->link_src_reg, &reg);
if (reg & BIT(pattern_gen->link_src_offset))
seq_printf(m, "%s\n", "on");
else
seq_printf(m, "%s\n", "off");
return 0;
}
static int pattern_gen_open(struct inode *inode, struct file *file)
{
struct pattern_gen *pattern_gen = inode->i_private;
return single_open(file, pattern_gen_show, pattern_gen);
}
static const struct file_operations pattern_gen_fops = {
.owner = THIS_MODULE,
.open = pattern_gen_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = pattern_gen_write,
};
void rkx110_x120_pattern_gen_debugfs_create_file(struct pattern_gen *pattern_gen,
struct rk_serdes_chip *chip,
struct dentry *dentry)
{
pattern_gen->chip = chip;
debugfs_create_file(pattern_gen->name, 0600, dentry, pattern_gen,
&pattern_gen_fops);
}

View File

@@ -0,0 +1,304 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*/
#include <linux/debugfs.h>
#include "hal/pinctrl_api.h"
#include "rkx110_x120.h"
#include "rkx110_reg.h"
#include "serdes_combphy.h"
#if defined(CONFIG_DEBUG_FS)
static struct pattern_gen rkx110_pattern_gen[] = {
{
.name = "dsi0",
.base = RKX110_PATTERN_GEN_DSI0_BASE,
.link_src_reg = SER_GRF_SOC_CON4,
.link_src_offset = 12,
}, {
.name = "dsi1",
.base = RKX110_PATTERN_GEN_DSI1_BASE,
.link_src_reg = SER_GRF_SOC_CON4,
.link_src_offset = 13,
}, {
.name = "lvds0",
.base = RKX110_PATTERN_GEN_LVDS0_BASE,
.link_src_reg = SER_GRF_SOC_CON4,
.link_src_offset = 14,
}, {
.name = "lvds1",
.base = RKX110_PATTERN_GEN_LVDS1_BASE,
.link_src_reg = SER_GRF_SOC_CON4,
.link_src_offset = 15,
},
{ /* sentinel */ }
};
static const struct rk_serdes_reg rkx110_regs[] = {
{
.name = "cru",
.reg_base = RKX110_SER_CRU_BASE,
.reg_len = 0xF04,
},
{
.name = "grf",
.reg_base = RKX110_SER_GRF_BASE,
.reg_len = 0x220,
},
{
.name = "grf_mipi0",
.reg_base = RKX110_GRF_MIPI0_BASE,
.reg_len = 0x600,
},
{
.name = "grf_mipi1",
.reg_base = RKX110_GRF_MIPI1_BASE,
.reg_len = 0x600,
},
{
.name = "mipi_lvds_phy0",
.reg_base = RKX110_MIPI_LVDS_RX_PHY0_BASE,
.reg_len = 0xb0,
},
{
.name = "mipi_lvds_phy1",
.reg_base = RKX110_MIPI_LVDS_RX_PHY1_BASE,
.reg_len = 0xb0,
},
{
.name = "host0",
.reg_base = RKX110_CSI2HOST0_BASE,
.reg_len = 0x60,
},
{
.name = "host1",
.reg_base = RKX110_CSI2HOST1_BASE,
.reg_len = 0x60,
},
{
.name = "vicap",
.reg_base = RKX110_VICAP_BASE,
.reg_len = 0x220,
},
{
.name = "gpio0",
.reg_base = RKX110_GPIO0_BASE,
.reg_len = 0x80,
},
{
.name = "gpio1",
.reg_base = RKX110_GPIO1_BASE,
.reg_len = 0x80,
},
{
.name = "dsi0",
.reg_base = RKX110_DSI_RX0_BASE,
.reg_len = 0x1D0,
},
{
.name = "dsi1",
.reg_base = RKX110_DSI_RX1_BASE,
.reg_len = 0x1D0,
},
{
.name = "rklink",
.reg_base = RKX110_SER_RKLINK_BASE,
.reg_len = 0xD4,
},
{
.name = "pcs0",
.reg_base = RKX110_SER_PCS0_BASE,
.reg_len = 0x1c0,
},
{
.name = "pcs1",
.reg_base = RKX110_SER_PCS1_BASE,
.reg_len = 0x1c0,
},
{
.name = "pma0",
.reg_base = RKX110_SER_PMA0_BASE,
.reg_len = 0x100,
},
{
.name = "pma1",
.reg_base = RKX110_SER_PMA1_BASE,
.reg_len = 0x100,
},
{
.name = "dsi0_pattern_gen",
.reg_base = RKX110_PATTERN_GEN_DSI0_BASE,
.reg_len = 0x18,
},
{
.name = "dsi1_pattern_gen",
.reg_base = RKX110_PATTERN_GEN_DSI1_BASE,
.reg_len = 0x18,
},
{
.name = "lvds0_pattern_gen",
.reg_base = RKX110_PATTERN_GEN_LVDS0_BASE,
.reg_len = 0x18,
},
{
.name = "lvds1_pattern_gen",
.reg_base = RKX110_PATTERN_GEN_LVDS1_BASE,
.reg_len = 0x18,
},
{ /* sentinel */ }
};
static int rkx110_reg_show(struct seq_file *s, void *v)
{
const struct rk_serdes_reg *regs = rkx110_regs;
struct rk_serdes_chip *chip = s->private;
struct rk_serdes *serdes = chip->serdes;
struct i2c_client *client = chip->client;
int i;
u32 val = 0;
seq_printf(s, "rkx110_%s:\n", file_dentry(s->file)->d_iname);
while (regs->name) {
if (!strcmp(regs->name, file_dentry(s->file)->d_iname))
break;
regs++;
}
if (!regs->name)
return -ENODEV;
for (i = 0; i <= regs->reg_len; i += 4) {
serdes->i2c_read_reg(client, regs->reg_base + i, &val);
if (i % 16 == 0)
seq_printf(s, "\n0x%04x:", i);
seq_printf(s, " %08x", val);
}
seq_puts(s, "\n");
return 0;
}
static ssize_t rkx110_reg_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
const struct rk_serdes_reg *regs = rkx110_regs;
struct rk_serdes_chip *chip = file->f_path.dentry->d_inode->i_private;
struct rk_serdes *serdes = chip->serdes;
struct i2c_client *client = chip->client;
u32 addr;
u32 val;
char kbuf[25];
int ret;
if (count >= sizeof(kbuf))
return -ENOSPC;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
ret = sscanf(kbuf, "%x%x", &addr, &val);
if (ret != 2)
return -EINVAL;
while (regs->name) {
if (!strcmp(regs->name, file_dentry(file)->d_iname))
break;
regs++;
}
if (!regs->name)
return -ENODEV;
addr += regs->reg_base;
serdes->i2c_write_reg(client, addr, val);
return count;
}
static int rkx110_reg_open(struct inode *inode, struct file *file)
{
struct rk_serdes_chip *chip = inode->i_private;
return single_open(file, rkx110_reg_show, chip);
}
static const struct file_operations rkx110_reg_fops = {
.owner = THIS_MODULE,
.open = rkx110_reg_open,
.read = seq_read,
.write = rkx110_reg_write,
.llseek = seq_lseek,
.release = single_release,
};
void rkx110_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry)
{
struct pattern_gen *pattern_gen = rkx110_pattern_gen;
const struct rk_serdes_reg *regs = rkx110_regs;
struct dentry *dir;
dir = debugfs_create_dir("registers", dentry);
if (!IS_ERR(dir)) {
while (regs->name) {
debugfs_create_file(regs->name, 0600, dir, chip, &rkx110_reg_fops);
regs++;
}
}
dir = debugfs_create_dir("pattern_gen", dentry);
if (!IS_ERR(dir)) {
while (pattern_gen->name) {
rkx110_x120_pattern_gen_debugfs_create_file(pattern_gen, chip, dir);
pattern_gen++;
}
}
}
#endif
static int rkx110_rgb_rx_iomux_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route)
{
struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
uint32_t pins;
pins = RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 | RK_SERDES_GPIO_PIN_C2 |
RK_SERDES_GPIO_PIN_C3 | RK_SERDES_GPIO_PIN_C4 | RK_SERDES_GPIO_PIN_C5 |
RK_SERDES_GPIO_PIN_C6 | RK_SERDES_GPIO_PIN_C7;
serdes->set_hwpin(serdes, client, PIN_RKX110, RK_SERDES_SER_GPIO_BANK0, pins,
RK_SERDES_PIN_CONFIG_MUX_FUNC1);
pins = RK_SERDES_GPIO_PIN_A0 | RK_SERDES_GPIO_PIN_A1 | RK_SERDES_GPIO_PIN_A2 |
RK_SERDES_GPIO_PIN_A3 | RK_SERDES_GPIO_PIN_A4 | RK_SERDES_GPIO_PIN_A5 |
RK_SERDES_GPIO_PIN_A6 | RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0 |
RK_SERDES_GPIO_PIN_B1 | RK_SERDES_GPIO_PIN_B2 | RK_SERDES_GPIO_PIN_B3 |
RK_SERDES_GPIO_PIN_B4 | RK_SERDES_GPIO_PIN_B5 | RK_SERDES_GPIO_PIN_B6 |
RK_SERDES_GPIO_PIN_B7 | RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 |
RK_SERDES_GPIO_PIN_C2 | RK_SERDES_GPIO_PIN_C3;
serdes->set_hwpin(serdes, client, PIN_RKX110, RK_SERDES_SER_GPIO_BANK1, pins,
RK_SERDES_PIN_CONFIG_MUX_FUNC1);
return 0;
}
int rkx110_rgb_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
{
rkx110_rgb_rx_iomux_cfg(serdes, route);
return 0;
}
int rkx110_lvds_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, int id)
{
rkx110_combrxphy_set_mode(serdes, COMBRX_PHY_MODE_VIDEO_LVDS);
rkx110_combrxphy_power_on(serdes, id ? COMBPHY_1 : COMBPHY_0);
return 0;
}

View File

@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#include "hal/cru_api.h"
#include <linux/kernel.h>
#include <linux/iopoll.h>
#include "rkx110_x120.h"
#include "rkx110_reg.h"
#include "serdes_combphy.h"
#define SOFT_RST 0x0000
#define HS_CLK_SOFT_RST BIT(1)
#define CFG_CLK_SOFT_RST BIT(0)
#define PHY_RCAL 0x0004
#define RCAL_DONE BIT(17)
#define RCAL_OUT(x) UPDATE(x, 16, 13)
#define RCAL_CTL(x) UPDATE(x, 12, 5)
#define RCAL_TRIM(x) UPDATE(x, 4, 1)
#define RCAL_EN BIT(0)
#define ULP_RX_EN 0x0008
#define VOFFCAL_OUT 0x000c
#define CSI_CLK_VOFFCAL_DONE BIT(29)
#define CSI_CLK_VOFFCAL_OUT(x) UPDATE(x, 28, 24)
#define CSI_0_VOFFCAL_DONE BIT(23)
#define CSI_0_VOFFCAL_OUT(x) UPDATE(x, 22, 18)
#define CSI_1_VOFFCAL_DONE BIT(17)
#define CSI_1_VOFFCAL_OUT(x) UPDATE(x, 16, 12)
#define CSI_2_VOFFCAL_DONE BIT(11)
#define CSI_2_VOFFCAL_OUT(x) UPDATE(x, 10, 6)
#define CSI_3_VOFFCAL_DONE BIT(5)
#define CSI_3_VOFFCAL_OUT(x) UPDATE(x, 4, 0)
#define CSI_CTL01 0x0010
#define CSI_CTL1(x) UPDATE(x, 31, 16)
#define CSI_CTL0(x) UPDATE(x, 15, 0)
#define CSI_CTL23 0x0014
#define CSI_CTL3(x) UPDATE(x, 31, 16)
#define CSI_CTL2(x) UPDATE(x, 15, 0)
#define CSI_VINIT 0x001c
#define CSI_LPRX_VREF_TRIM UPDATE(x, 23, 20)
#define CSI_CLK_LPRX_VINIT(x) UPDATE(x, 19, 16)
#define CSI_3_LPRX_VINIT(x) UPDATE(x, 15, 12)
#define CSI_2_LPRX_VINIT(x) UPDATE(x, 11, 8)
#define CSI_1_LPRX_VINIT(x) UPDATE(x, 7, 4)
#define CSI_0_LPRX_VINIT(x) UPDATE(x, 3, 0)
#define CLANE_PARA 0x0020
#define T_CLK_TERM_EN(x) UPDATE(x, 15, 8)
#define T_CLK_SETTLE(x) UPDATE(x, 7, 0)
#define T_HS_TERMEN 0x0024
#define T_D3_TERMEN(x) UPDATE(x, 31, 24)
#define T_D2_TERMEN(x) UPDATE(x, 23, 16)
#define T_D1_TERMEN(x) UPDATE(x, 15, 8)
#define T_D0_TERMEN(x) UPDATE(x, 7, 0)
#define T_HS_SETTLE 0x0028
#define T_D3_SETTLE(x) UPDATE(x, 31, 24)
#define T_D2_SETTLE(x) UPDATE(x, 23, 16)
#define T_D1_SETTLE(x) UPDATE(x, 15, 8)
#define T_D0_SETTLE(x) UPDATE(x, 7, 0)
#define T_CLANE_INIT 0x0030
#define T_CLK_INIT(x) UPDATE(x, 23, 0)
#define T_LANE0_INIT 0x0034
#define T_D0_INIT(x) UPDATE(x, 23, 0)
#define T_LANE1_INIT 0x0038
#define T_D1_INIT(x) UPDATE(x, 23, 0)
#define T_LANE2_INIT 0x003c
#define T_D2_INIT(x) UPDATE(x, 23, 0)
#define T_LANE3_INIT 0x0040
#define T_D3_INIT(x) UPDATE(x, 23, 0)
#define TLPX_CTRL 0x0044
#define EN_TLPX_CHECK BIT(8)
#define TLPX(x) UPDATE(x, 7, 0)
#define NE_SWAP 0x0048
#define DPDN_SWAP_LANE(x) UPDATE(1 << x, 11, 8)
#define LANE_SWAP_LAN3(x) UPDATE(x, 7, 6)
#define LANE_SWAP_LANE2(x) UPDATE(x, 5, 4)
#define LANE_SWAP_LANE1(x) UPDATE(x, 3, 2)
#define LANE_SWAP_LANE0(x) UPDATE(x, 1, 0)
#define MISC_INFO 0x004c
#define ULPS_LP10_SEL BIT(1)
#define LONG_SOTSYNC_EN BIT(0)
#define GRF_MIPI_RX_CON0 0x0000
#define RXCK_RTRM(x) HIWORD_UPDATE(x, GENMASK(15, 12), 12)
#define LVDS_RX3_PD(x) HIWORD_UPDATE(x, BIT(10), 10)
#define LVDS_RX2_PD(x) HIWORD_UPDATE(x, BIT(9), 9)
#define LVDS_RX1_PD(x) HIWORD_UPDATE(x, BIT(8), 8)
#define LVDS_RX0_PD(x) HIWORD_UPDATE(x, BIT(7), 7)
#define LVDS_RXCK_PD(x) HIWORD_UPDATE(x, BIT(6), 6)
#define LANE3_ENABLE(x) HIWORD_UPDATE(x, BIT(5), 5)
#define LANE2_ENABLE(x) HIWORD_UPDATE(x, BIT(4), 4)
#define LANE1_ENABLE(x) HIWORD_UPDATE(x, BIT(3), 3)
#define LANE0_ENABLE(x) HIWORD_UPDATE(x, BIT(2), 2)
#define PHY_MODE(x) HIWORD_UPDATE(x, GENMASK(1, 0), 0)
#define GRF_MIPI_RX_CON2 0x0008
#define RXCK_CTL_5(x) HIWORD_UPDATE(x, BIT(11), 11)
#define DDR_CLK_DUTY_CYCLE(x) HIWORD_UPDATE(x, GENMASK(10, 8), 8)
#define BUS_WIDTH_SELECTION(x) HIWORD_UPDATE(x, GENMASK(7, 5), 5)
#define RXCK_CTL_2(x) HIWORD_UPDATE(x, BIT(4), 4)
#define RXCK_CTL_1(x) HIWORD_UPDATE(x, GENMASK(2, 1), 1)
#define RXCK_CTL_0(x) HIWORD_UPDATE(x, BIT(0), 0)
#define GRF_MIPI_RX_CON4 0x0010
#define GRF_MIPI_RX_CON5 0x0014
#define GRF_MIPI_RX_CON6 0x0018
#define GRF_MIPI_RX_CON7 0x001c
#define GRF_SOC_STATUS 0x0080
#define DLL_LOCK BIT(24)
#define LVDS1_MSBSEL(x) HIWORD_UPDATE(x, BIT(5), 5)
#define LVDS0_MSBSEL(x) HIWORD_UPDATE(x, BIT(2), 2)
static void
rkx110_combrxphy_dsi_timing_init(struct rk_serdes *ser, enum comb_phy_id id)
{
}
static void rkx110_combrxphy_dsi_power_on(struct rk_serdes *ser, enum comb_phy_id id)
{
struct hwclk *hwclk = ser->chip[DEVICE_LOCAL].hwclk;
struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
u32 val = 0;
u32 grf_base;
switch (id) {
case COMBPHY_0:
hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0, 100 * USEC_PER_SEC);
dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY0:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0));
break;
case COMBPHY_1:
hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1, 100 * USEC_PER_SEC);
dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY1:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1));
break;
default:
break;
}
serdes_combphy_get_default_config(combrxphy->rate,
&combrxphy->mipi_dphy_cfg);
switch (ser->dsi_rx.lanes) {
case 4:
val |= LANE3_ENABLE(1);
fallthrough;
case 3:
val |= LANE2_ENABLE(1);
fallthrough;
case 2:
val |= LANE1_ENABLE(1);
fallthrough;
case 1:
default:
val |= LANE0_ENABLE(1);
break;
}
grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
PHY_MODE(COMBRX_PHY_MODE_VIDEO_MIPI) | val);
rkx110_combrxphy_dsi_timing_init(ser, id);
}
static void rkx110_combrxphy_dsi_power_off(struct rk_serdes *ser, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
u32 grf_base;
grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LANE3_ENABLE(0) | LANE2_ENABLE(0) |
LANE1_ENABLE(0) | LANE0_ENABLE(0));
}
static void rkx110_combrxphy_lvds_power_on(struct rk_serdes *ser, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
u32 val;
int ret;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RXCK_PD(0) | PHY_MODE(COMBRX_PHY_MODE_VIDEO_LVDS));
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON2,
BUS_WIDTH_SELECTION(7) | RXCK_CTL_2(1) |
RXCK_CTL_1(1) | RXCK_CTL_0(0));
ser->i2c_write_reg(client, SER_GRF_SOC_CON2,
id ? LVDS1_MSBSEL(0) : LVDS0_MSBSEL(0));
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON4, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON5, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON6, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON7, 0xffff0f08);
ret = read_poll_timeout(ser->i2c_read_reg, ret,
!(ret < 0) && (val & DLL_LOCK),
0, MSEC_PER_SEC, false, client,
grf_base + GRF_SOC_STATUS, &val);
if (ret < 0)
dev_err(ser->dev, "DLL is not locked\n");
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RX3_PD(0) | LVDS_RX2_PD(0) |
LVDS_RX1_PD(0) | LVDS_RX0_PD(0));
}
static void rkx110_combrxphy_lvds_power_off(struct rk_serdes *ser, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RX3_PD(1) | LVDS_RX2_PD(1) |
LVDS_RX1_PD(1) | LVDS_RX0_PD(1));
}
static void rkx110_combrxphy_lvds_camera_power_on(struct rk_serdes *ser, enum comb_phy_id id)
{
}
static void rkx110_combrxphy_lvds_camera_power_off(struct rk_serdes *ser, enum comb_phy_id id)
{
}
void rkx110_combrxphy_power_on(struct rk_serdes *ser, enum comb_phy_id id)
{
struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
switch (combrxphy->mode) {
case COMBRX_PHY_MODE_VIDEO_MIPI:
rkx110_combrxphy_dsi_power_on(ser, id);
break;
case COMBRX_PHY_MODE_VIDEO_LVDS:
rkx110_combrxphy_lvds_power_on(ser, id);
break;
case COMBRX_PHY_MODE_LVDS_CAMERA:
rkx110_combrxphy_lvds_camera_power_on(ser, id);
break;
default:
break;
}
}
void rkx110_combrxphy_power_off(struct rk_serdes *ser, enum comb_phy_id id)
{
struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
switch (combrxphy->mode) {
case COMBRX_PHY_MODE_VIDEO_MIPI:
rkx110_combrxphy_dsi_power_off(ser, id);
break;
case COMBRX_PHY_MODE_VIDEO_LVDS:
rkx110_combrxphy_lvds_power_off(ser, id);
break;
case COMBRX_PHY_MODE_LVDS_CAMERA:
rkx110_combrxphy_lvds_camera_power_off(ser, id);
break;
default:
break;
}
}
void rkx110_combrxphy_set_rate(struct rk_serdes *ser, u64 rate)
{
struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
combrxphy->rate = rate;
}
void rkx110_combrxphy_set_mode(struct rk_serdes *ser, enum combrx_phy_mode mode)
{
struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
combrxphy->mode = mode;
}

View File

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#include <asm/unaligned.h>
#include <dt-bindings/mfd/rockchip-serdes.h>
#include "rkx110_reg.h"
#include "rkx110_x120.h"
#include "rkx110_dsi_rx.h"
#include "serdes_combphy.h"
#define DSI_RX_MIPI_IDX_CTRL0(x) (0x0100 + x * 8)
#define SW_COMMAND_MODE_EN BIT(26)
#define SW_DT(x) UPDATE(x, 15, 10)
#define SW_VC(x) UPDATE(x, 9, 8)
#define SW_CAP_EN BIT(0)
#define DSI_RX_MIPI_IDX_CTRL1(x) (0X0104 + x * 8)
#define SW_HEIGHT(x) UPDATE(x, 29, 16)
#define SW_WIDTH(x) UPDATE(x, 13, 0)
#define DSI_RX_MIPI_INTEN 0x0174
#define DSI_RX_MIPI_INTSTAT 0x0178
#define DSI_RX_MIPI_SIZE_NUM(x) (0x01c0 + x * 4)
enum {
VSYNC_START = 0x01,
VSYNC_END = 0x11,
HSYNC_START = 0x21,
HSYNC_END = 0x31,
};
static inline int dsi_rx_write(struct rk_serdes *ser, u32 reg, u32 val)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
return ser->i2c_write_reg(client, reg, val);
}
static inline int dsi_rx_read(struct rk_serdes *ser, u32 reg, u32 *val)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
return ser->i2c_read_reg(client, reg, val);
}
static inline int dsi_rx_update_bits(struct rk_serdes *ser,
u32 reg, u32 mask, u32 val)
{
struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
return ser->i2c_update_bits(client, reg, mask, val);
}
void rkx110_dsi_rx_enable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
{
struct rkx110_dsi_rx *dsi = &ser->dsi_rx;
const struct videomode *vm = &route->vm;
unsigned long pixelclock;
u32 hactive, vactive;
u64 rate;
u32 val = 0;
u32 csi_base, dsirx_base;
switch (route->frame_mode) {
case SERDES_SP_PIXEL_INTERLEAVED:
fallthrough;
case SERDES_SP_LEFT_RIGHT_SPLIT:
pixelclock = vm->pixelclock * 2;
hactive = vm->hactive * 2;
vactive = vm->vactive;
break;
case SERDES_SP_LINE_INTERLEAVED:
pixelclock = vm->pixelclock * 2;
vactive = vm->vactive * 2;
hactive = vm->hactive;
break;
case SERDES_FRAME_NORMAL_MODE:
fallthrough;
default:
pixelclock = vm->pixelclock;
hactive = vm->hactive;
vactive = vm->vactive;
break;
}
rate = DIV_ROUND_CLOSEST_ULL(pixelclock, dsi->lanes);
rkx110_combrxphy_set_mode(ser, COMBRX_PHY_MODE_VIDEO_MIPI);
rkx110_combrxphy_set_rate(ser, rate * MSEC_PER_SEC);
rkx110_combrxphy_power_on(ser, id ? COMBPHY_1 : COMBPHY_0);
csi_base = id ? RKX110_CSI2HOST1_BASE : RKX110_CSI2HOST0_BASE;
dsirx_base = id ? RKX110_DSI_RX1_BASE : RKX110_DSI_RX0_BASE;
dsi_rx_write(ser, csi_base + CSI2HOST_N_LANES, dsi->lanes - 1);
dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 0);
val |= SW_DSI_EN | SW_DATETYPE_FE(VSYNC_END) | SW_DATETYPE_FS(VSYNC_START);
dsi_rx_update_bits(ser, csi_base + CSI2HOST_CONTROL,
SW_DATETYPE_FE_MASK |
SW_DATETYPE_FS_MASK |
SW_DSI_EN, val);
dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 1);
val = SW_CAP_EN | SW_VC(0);
/*
* video mode only support rgb888(0x3e), command mode
* only support DCS Long Write(0x39)
*/
val |= (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO) ?
(0 | SW_DT(0x3e)) : (SW_COMMAND_MODE_EN | SW_DT(0x39));
dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL0(0), val);
dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL1(0),
SW_HEIGHT(vactive) | SW_WIDTH(hactive));
}
void rkx110_dsi_rx_disable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
{
rkx110_combrxphy_power_off(ser, id ? COMBPHY_1 : COMBPHY_0);
}

View File

@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#ifndef _RKX110_DSI_RX_H
#define _RKX110_DSI_RX_H
void rkx110_dsi_rx_enable(struct rk_serdes *ser, struct rk_serdes_route *route, int id);
void rkx110_dsi_rx_disable(struct rk_serdes *ser, struct rk_serdes_route *route, int id);
#endif

View File

@@ -0,0 +1,738 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Zhang Yubing <yubing.zhang@rock-chips.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/mfd/core.h>
#include <dt-bindings/mfd/rockchip-serdes.h>
#include "hal/cru_api.h"
#include "hal/pinctrl_api.h"
#include "rkx110_x120.h"
#include "rkx110_reg.h"
#define LINK_REG(x) ((x) + RKX110_SER_RKLINK_BASE)
#define RKLINK_TX_SERDES_CTRL LINK_REG(0x0000)
#define VIDEO_CH0_EN_MASK BIT(31)
#define VIDEO_CH1_EN_MASK BIT(30)
#define STOP_AUDIO BIT(18)
#define STOP_VIDEO_CH1 BIT(17)
#define STOP_VIDEO_CH0 BIT(16)
#define CH1_LVDS_SEL_EN BIT(15)
#define TRAIN_CLK_SEL_MASK GENMASK(14, 13)
#define TRAIN_CLK_SEL_CH0 UPDATE(0, 14, 13)
#define TRAIN_CLK_SEL_CH1 UPDATE(1, 14, 13)
#define TRAIN_CLK_SEL_I2S UPDATE(2, 14, 13)
#define STREAM_TYPE_MASK BIT(12)
#define SER_STREAM_CAMERA UPDATE(0, 12, 12)
#define SER_STREAM_DISPLAY UPDATE(1, 12, 12)
#define VIDEO_EN BIT(11)
#define SERDES_LANE_SWAP_EN BIT(10)
#define SERDES_MIRROR_EN BIT(9)
#define SER_CH1_EN BIT(8)
#define CH1_DSOURCE_ID(x) UPDATE(x, 7, 6)
#define CH0_DSOURCE_ID(x) UPDATE(x, 5, 4)
#define SERDES_DATA_WIDTH_MASK GENMASK(3, 2)
#define SERDES_DATA_WIDTH_8BIT UPDATE(0, 3, 2)
#define SERDES_DATA_WIDTH_16BIT UPDATE(1, 3, 2)
#define SERDES_DATA_WIDTH_24BIT UPDATE(2, 3, 2)
#define SERDES_DATA_WIDTH_32BIT UPDATE(3, 3, 2)
#define SERDES_DUAL_LANE_EN BIT(1)
#define SER_EN BIT(0)
#define RKLINK_TX_VIDEO_CTRL LINK_REG(0x0004)
#define VIDEO_REPKT_LENGTH(x) UPDATE(x, 29, 16)
#define DUAL_LVDS_CYCLE_DIFF(x) UPDATE(x, 13, 4)
#define PIXEL_VSYNC_SEL BIT(3)
#define SOURCE_ID_MASK UPDATE(7, 2, 0)
#define CAMERA_SRC_CSI UPDATE(0, 2, 0)
#define CAMERA_SRC_LVDS UPDATE(1, 2, 0)
#define CAMERA_SRC_DVP UPDATE(2, 2, 0)
#define DISPLAY_SRC_DSI UPDATE(0, 2, 0)
#define DISPLAY_SRC_DUAL_LDVS UPDATE(1, 2, 0)
#define DISPLAY_SRC_LVDS0 UPDATE(2, 2, 0)
#define DISPLAY_SRC_LVDS1 UPDATE(3, 2, 0)
#define DISPLAY_SRC_RGB UPDATE(5, 2, 0)
#define SER_RKLINK_DSI_REC0(x) LINK_REG(0x0008 + 0x10 * x)
#define DSI_REC_START BIT(31)
#define DSI_CMD_TYPE BIT(30)
#define DSI_HACT(x) UPDATE(x, 29, 16)
#define DSI_VACT(x) UPDATE(x, 13, 0)
#define SER_RKLINK_DSI_REC1(x) LINK_REG(0x000C + 0x10 * x)
#define DSI_SPLIT_LR_SWAP BIT(30)
#define DSI_FRAME_MODE(x) UPDATE(x, 29, 28)
#define DSI_HFP(x) UPDATE(x, 27, 16)
#define DSI_HBP(x) UPDATE(x, 11, 0)
#define SER_RKLINK_DSI_REC2(x) LINK_REG(0x0010 + 0x10 * x)
#define DSI_0_DST_MASK GENMASK(31, 30)
#define DSI_0_DST(x) UPDATE(x, 31, 30)
#define DSI_CHANNEL_SWAP UPDATE(2, 31, 30)
#define DSI0_SPLIT_MODE UPDATE(0, 31, 30)
#define DSI1_SPLIT_MODE UPDATE(3, 31, 30)
#define DSI_VFP(x) UPDATE(x, 29, 20)
#define DSI_VBP(x) UPDATE(x, 19, 10)
#define DSI_VSA(x) UPDATE(x, 9, 0)
#define SER_RKLINK_DSI_REC3(x) LINK_REG(0x0014 + 0x10 * x)
#define DSI_DELAY_LENGTH(x) UPDATE(x, 31, 12)
#define DSI_HSA(x) UPDATE(x, 11, 0)
#define SER_RKLINK_AUDIO_CRTL LINK_REG(0x0028)
#define SER_RKLINK_AUDIO_CTRL LINK_REG(0x0028)
#define SER_RKLINK_AUDIO_FDIV LINK_REG(0x002C)
#define SER_RKLINK_AUDIO_LRCK LINK_REG(0x0030)
#define SER_RKLINK_AUDIO_RECOVER LINK_REG(0x0034)
#define SER_RKLINK_AUDIO_FM_STATUS LINK_REG(0x0038)
#define SER_RKLINK_FIFO_STATUS LINK_REG(0x003C)
#define SER_RKLINK_SOURCE_IRQ_EN LINK_REG(0x0040)
#define SER_RKLINK_TRAIN_CTRL LINK_REG(0x0044)
#define SER_RKLINK_I2C_CFG LINK_REG(0x00C0)
#define SER_RKLINK_SPI_CFG LINK_REG(0x00C4)
#define SER_RKLINK_UART_CFG LINK_REG(0x00C8)
#define SER_RKLINK_GPIO_CFG LINK_REG(0x00CC)
#define GPIO_GROUP1_EN BIT(17)
#define GPIO_GROUP0_EN BIT(16)
#define SER_RKLINK_IO_DEF_INV_CFG LINK_REG(0x00D0)
#define OTHER_LANE_ACTIVE(x) HIWORD_UPDATE(x, 12, 12)
#define FORWARD_NON_ACK(x) HIWORD_UPDATE(x, 11, 11)
#define SER_RKLINK_DISP_DSI_SPLIT BIT(1)
#define SER_RKLINK_DISP_MIRROR BIT(2)
#define SER_RKLINK_DISP_DUAL_CHANNEL BIT(3)
#define PCS_REG(id, x) ((x) + RKX110_SER_PCS0_BASE + (id) * RKX110_SER_PCS_OFFSET)
#define PCS_REG00(id) PCS_REG(id, 0x00)
#define PCS_DUAL_LANE_MODE_EN HIWORD_UPDATE(1, GENMASK(12, 12), 12)
#define PCS_AUTO_START_EN HIWORD_UPDATE(1, GENMASK(3, 3), 3)
#define PCS_EN_MASK HIWORD_MASK(2, 2)
#define PCS_EN HIWORD_UPDATE(1, GENMASK(2, 2), 2)
#define PCS_DISABLE HIWORD_UPDATE(0, GENMASK(2, 2), 2)
#define PCS_ECU_MODE HIWORD_UPDATE(0, GENMASK(2, 2), 1)
#define PCS_REMOTE_MODE HIWORD_UPDATE(1, GENMASK(2, 2), 1)
#define PCS_SAFE_MODE_EN HIWORD_UPDATE(1, GENMASK(0, 0), 0)
#define PCS_REG04(id) PCS_REG(id, 0x04)
#define PCS_REG08(id) PCS_REG(id, 0x08)
#define VIDEO_SUS_EN(x) HIWORD_UPDATE(x, GENMASK(15, 15), 15)
#define PCS_REG10(id) PCS_REG(id, 0x10)
#define PCS_REG14(id) PCS_REG(id, 0x14)
#define PCS_REG18(id) PCS_REG(id, 0x18)
#define PCS_REG1C(id) PCS_REG(id, 0x1C)
#define PCS_REG20(id) PCS_REG(id, 0x20)
#define PCS_REG24(id) PCS_REG(id, 0x24)
#define PCS_REG28(id) PCS_REG(id, 0x28)
#define PCS_REG30(id) PCS_REG(id, 0x30)
#define PCS_REG34(id) PCS_REG(id, 0x34)
#define PCS_REG40(id) PCS_REG(id, 0x40)
#define PMA_REG(id, x) ((x) + RKX110_SER_PMA0_BASE + (id) * RKX110_SER_PMA_OFFSET)
#define SER_PMA_STATUS(id) PMA_REG(id, 0x00)
#define SER_PMA_FORCE_INIT_STA BIT(8)
#define SER_PMA_CTRL(id) PMA_REG(id, 0x04)
#define SER_PMA_FORCE_INIT_MASK HIWORD_MASK(8, 8)
#define SER_PMA_FORCE_INIT_EN HIWORD_UPDATE(1, BIT(8), 8)
#define SER_PMA_FORCE_INIT_DISABLE HIWORD_UPDATE(0, BIT(8), 8)
#define SER_PMA_DUAL_CHANNEL HIWORD_UPDATE(1, BIT(3), 3)
#define SER_PMA_INIT_CNT_CLR_MASK HIWORD_MASK(2, 2)
#define SER_PMA_INIT_CNT_CLR HIWORD_UPDATE(1, BIT(2), 2)
#define SER_PMA_LOAD00(id) PMA_REG(id, 0x10)
#define PMA_RATE_MODE_MASK HIWORD_MASK(2, 0)
#define PMA_FDR_MODE HIWORD_UPDATE(1, GENMASK(2, 0), 0)
#define PMA_HDR_MODE HIWORD_UPDATE(2, GENMASK(2, 0), 0)
#define PMA_QDR_MODE HIWORD_UPDATE(4, GENMASK(2, 0), 0)
#define SER_PMA_LOAD01(id) PMA_REG(id, 0x14)
#define SER_PMA_LOAD02(id) PMA_REG(id, 0x18)
#define SER_PMA_LOAD03(id) PMA_REG(id, 0x1C)
#define SER_PMA_LOAD04(id) PMA_REG(id, 0x20)
#define PMA_PLL_DIV_MASK HIWORD_MASK(14, 0)
#define PLL_I_POST_DIV(x) HIWORD_UPDATE(x, GENMASK(14, 10), 10)
#define PLL_F_POST_DIV(x) HIWORD_UPDATE(x, GENMASK(9, 0), 0)
#define PMA_PLL_DIV(x) HIWORD_UPDATE(x, GENMASK(14, 0), 0)
#define SER_PMA_LOAD05(id) PMA_REG(id, 0x2C)
#define PMA_PLL_REFCLK_DIV_MASK HIWORD_MASK(3, 0)
#define PMA_PLL_REFCLK_DIV(x) HIWORD_UPDATE(x, GENMASK(3, 0), 0)
#define SER_PMA_LOAD06(id) PMA_REG(id, 0x28)
#define SER_PMA_LOAD07(id) PMA_REG(id, 0x2C)
#define SER_PMA_LOAD08(id) PMA_REG(id, 0x30)
#define PMA_FCK_VCO_MASK HIWORD_MASK(15, 15)
#define PMA_FCK_VCO HIWORD_UPDATE(1, BIT(15), 15)
#define PMA_FCK_VCO_DIV2 HIWORD_UPDATE(0, BIT(15), 15)
#define SER_PMA_LOAD09(id) PMA_REG(id, 0x34)
#define PMA_PLL_DIV4_MASK HIWORD_MASK(12, 12)
#define PMA_PLL_DIV4 HIWORD_UPDATE(1, GENMASK(12, 12), 12)
#define PMA_PLL_DIV8 HIWORD_UPDATE(0, GENMASK(12, 12), 12)
#define SER_PMA_LOAD0A(id) PMA_REG(id, 0x38)
#define PMA_CLK_8X_DIV_MASK HIWORD_MASK(7, 1)
#define PMA_CLK_8X_DIV(x) HIWORD_UPDATE(x, GENMASK(7, 1), 1)
enum {
SER_LINK_CH_ID0 = 0,
SER_LINK_CH_ID1,
SER_LINK_CH_ID2,
SER_LINK_CH_ID3,
};
static const struct rk_serdes_pt ser_pt[] = {
{
/* gpi_gpo_0 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x10001,
.en_val = 0x10001,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x10,
.dir_val = 0x10,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_1 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x10002,
.en_val = 0x10002,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x20,
.dir_val = 0x20,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A6,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_2 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x10004,
.en_val = 0x10004,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x40,
.dir_val = 0x40,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A7,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_3 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x10008,
.en_val = 0x10008,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x80,
.dir_val = 0x80,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_4 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x20100,
.en_val = 0x20100,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x1000,
.dir_val = 0x1000,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B3,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* gpi_gpo_5 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x20200,
.en_val = 0x20200,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x2000,
.dir_val = 0x2000,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B4,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* gpi_gpo_6 */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x20400,
.en_val = 0x20400,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x4000,
.dir_val = 0x4000,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B5,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough irq */
.en_reg = SER_RKLINK_GPIO_CFG,
.en_mask = 0x20800,
.en_val = 0x20800,
.dir_reg = SER_RKLINK_GPIO_CFG,
.dir_mask = 0x8000,
.dir_val = 0x8000,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A4,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
},
},
}, {
/* passthrough uart0 */
.en_reg = SER_RKLINK_UART_CFG,
.en_mask = 0x1,
.en_val = 0x1,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough uart1 */
.en_reg = SER_RKLINK_UART_CFG,
.en_mask = 0x2,
.en_val = 0x2,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough spi */
.en_reg = SER_RKLINK_SPI_CFG,
.en_mask = 0x4,
.en_val = 0x4,
.dir_reg = SER_RKLINK_SPI_CFG,
.dir_mask = 0x1,
.dir_val = 0x0,
.configs = 1,
{
{
.bank = RK_SERDES_SER_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6 |
RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
},
},
},
};
static int rk_serdes_get_stream_source(struct rk_serdes *serdes, int local_port)
{
if (serdes->stream_type == STREAM_DISPLAY) {
if (local_port & RK_SERDES_RGB_RX)
return DISPLAY_SRC_RGB;
else if (local_port & RK_SERDES_LVDS_RX0)
return DISPLAY_SRC_LVDS0;
else if (local_port & RK_SERDES_LVDS_RX1)
return DISPLAY_SRC_LVDS1;
else if (local_port & RK_SERDES_DUAL_LVDS_RX)
return DISPLAY_SRC_DUAL_LDVS;
else if (local_port & RK_SERDES_DSI_RX0)
return DISPLAY_SRC_DSI;
else if (local_port & RK_SERDES_DSI_RX1)
return DISPLAY_SRC_DSI;
} else {
return CAMERA_SRC_CSI;
}
return 0;
}
void rkx110_set_stream_source(struct rk_serdes *serdes, int local_port, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
u32 val, rx_src;
serdes->i2c_read_reg(client, RKLINK_TX_VIDEO_CTRL, &val);
val &= ~SOURCE_ID_MASK;
rx_src = rk_serdes_get_stream_source(serdes, local_port);
val |= rx_src;
serdes->i2c_write_reg(client, RKLINK_TX_VIDEO_CTRL, val);
}
static int rk_serdes_link_tx_ctrl_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route,
u8 remote_id)
{
struct hwclk *hwclk = serdes->chip[remote_id].hwclk;
struct i2c_client *client;
u32 ctrl_val, val;
u32 rx_src;
u32 stream_type;
if (route->stream_type == STREAM_DISPLAY) {
client = serdes->chip[DEVICE_LOCAL].client;
stream_type = SER_STREAM_DISPLAY;
} else {
client = serdes->chip[remote_id].client;
stream_type = SER_STREAM_CAMERA;
}
serdes->i2c_read_reg(client, RKLINK_TX_SERDES_CTRL, &ctrl_val);
ctrl_val &= ~(SER_EN | SERDES_DUAL_LANE_EN | SER_CH1_EN | SERDES_MIRROR_EN |
CH1_LVDS_SEL_EN);
ctrl_val |= stream_type;
if (serdes->route_flag & ROUTE_MULTI_LANE)
ctrl_val |= SERDES_DUAL_LANE_EN;
if (serdes->route_flag & ROUTE_MULTI_CHANNEL)
ctrl_val |= SER_CH1_EN;
if (serdes->route_flag & ROUTE_MULTI_MIRROR)
ctrl_val |= SERDES_MIRROR_EN;
if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
ctrl_val |= CH1_LVDS_SEL_EN;
serdes->i2c_write_reg(client, RKLINK_TX_SERDES_CTRL, ctrl_val);
serdes->i2c_read_reg(client, RKLINK_TX_VIDEO_CTRL, &val);
rx_src = rk_serdes_get_stream_source(serdes, route->local_port0);
val |= rx_src;
serdes->i2c_write_reg(client, RKLINK_TX_VIDEO_CTRL, val);
if (route->local_port0 & RK_SERDES_DUAL_LVDS_RX) {
hwclk_set_rate(hwclk, RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, route->vm.pixelclock);
dev_info(serdes->dev, "RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_CLK_2X_LVDS_RKLINK_TX));
}
ctrl_val |= SER_EN;
serdes->i2c_write_reg(client, RKLINK_TX_SERDES_CTRL, ctrl_val);
return 0;
}
static int rk_serdes_link_tx_dsi_enable(struct rk_serdes *serdes, struct rk_serdes_route *route,
int id)
{
struct videomode *vm = &route->vm;
struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
struct hwclk *hwclk = serdes->chip[DEVICE_LOCAL].hwclk;
int delay_length;
u32 value, type;
if (id == 0) {
hwclk_set_rate(hwclk, RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX,
route->vm.pixelclock);
dev_info(serdes->dev, "RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX));
} else if (id == 1) {
hwclk_set_rate(hwclk, RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX,
route->vm.pixelclock);
dev_info(serdes->dev, "RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX));
} else {
return 0;
}
/* config SER_RKLINK_DSI_REC1 */
value = DSI_FRAME_MODE(route->frame_mode);
value |= DSI_HFP(vm->hfront_porch);
value |= DSI_HBP(vm->hback_porch);
serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC1(id), value);
/* config SER_RKLINK_DSI_REC2 */
value = DSI_VFP(vm->vfront_porch);
value |= DSI_VBP(vm->vback_porch);
value |= DSI_VSA(vm->vsync_len);
serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC2(id), value);
if (id)
type = (serdes->route_flag & ROUTE_MULTI_CHANNEL) ? DSI_0_DST(0) : DSI_0_DST(2);
else
type = (serdes->route_flag & ROUTE_MULTI_CHANNEL) ? DSI_0_DST(3) : DSI_0_DST(1);
serdes->i2c_update_bits(client, SER_RKLINK_DSI_REC2(0), DSI_0_DST_MASK, type);
/* config SER_RKLINK_DSI_REC3 */
if (serdes->dsi_rx.mode_flags & SERDES_MIPI_DSI_MODE_VIDEO)
delay_length = vm->hsync_len + vm->hback_porch +
vm->hactive + vm->hfront_porch;
else
delay_length = (vm->vfront_porch + 1) * (vm->hsync_len +
vm->hback_porch + vm->hactive + vm->hfront_porch);
value = DSI_DELAY_LENGTH(delay_length);
value |= DSI_HSA(vm->hsync_len);
serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC3(id), value);
/* config SER_RKLINK_DSI_REC0 */
value = DSI_REC_START;
if (!(serdes->dsi_rx.mode_flags & SERDES_MIPI_DSI_MODE_VIDEO))
value |= DSI_CMD_TYPE;
value |= DSI_HACT(vm->hactive);
value |= DSI_VACT(vm->vactive);
serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC0(id), value);
return 0;
}
static int rk110_linktx_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route)
{
u8 remote_id = 0;
rk_serdes_link_tx_ctrl_enable(serdes, route, remote_id);
if (route->local_port0 & RK_SERDES_DSI_RX0) {
rk_serdes_link_tx_dsi_enable(serdes, route, 0);
if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
rk_serdes_link_tx_dsi_enable(serdes, route, 1);
}
if (route->local_port0 & RK_SERDES_DSI_RX1) {
rk_serdes_link_tx_dsi_enable(serdes, route, 1);
if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
rk_serdes_link_tx_dsi_enable(serdes, route, 0);
}
return 0;
}
static int rk110_ser_pcs_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 pcs_id)
{
struct i2c_client *client;
u8 remote_id = 0;
if (route->stream_type == STREAM_DISPLAY)
client = serdes->chip[DEVICE_LOCAL].client;
else
client = serdes->chip[remote_id].client;
if (serdes->version == SERDES_V1)
serdes->i2c_write_reg(client, PCS_REG08(pcs_id), VIDEO_SUS_EN(0));
return 0;
}
static int rk110_ser_pma_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 pcs_id)
{
return 0;
}
int rkx110_linktx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
{
rk110_linktx_cfg(serdes, route);
rk110_ser_pcs_cfg(serdes, route, 0);
rk110_ser_pma_cfg(serdes, route, 0);
if (serdes->route_flag & ROUTE_MULTI_LANE) {
rk110_ser_pcs_cfg(serdes, route, 1);
rk110_ser_pma_cfg(serdes, route, 1);
}
return 0;
}
void rkx110_linktx_video_enable(struct rk_serdes *serdes, u8 dev_id, bool enable)
{
struct i2c_client *client = serdes->chip[dev_id].client;
serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, VIDEO_EN, enable ? VIDEO_EN : 0);
}
void rkx110_linktx_channel_enable(struct rk_serdes *serdes, u8 ch_id, u8 dev_id, bool enable)
{
struct i2c_client *client = serdes->chip[dev_id].client;
if (ch_id)
serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, STOP_VIDEO_CH1,
enable ? 0 : STOP_VIDEO_CH1);
else
serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, STOP_VIDEO_CH0,
enable ? 0 : STOP_VIDEO_CH0);
}
void rkx110_linktx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
bool is_rx)
{
struct i2c_client *client = serdes->chip[client_id].client;
const struct rk_serdes_pt_pin *pt_pin = ser_pt[func_id].pt_pins;
int i;
/* config link passthrough */
serdes->i2c_update_bits(client, ser_pt[func_id].en_reg, ser_pt[func_id].en_mask,
ser_pt[func_id].en_val);
if (ser_pt[func_id].en_reg)
serdes->i2c_update_bits(client, ser_pt[func_id].dir_reg, ser_pt[func_id].dir_mask,
is_rx ? ser_pt[func_id].dir_val : ~ser_pt[func_id].dir_val);
/* config passthrough pinctrl */
for (i = 0; i < ser_pt[func_id].configs; i++) {
serdes->set_hwpin(serdes, client, PIN_RKX110, pt_pin[i].bank, pt_pin[i].pin,
is_rx ? pt_pin[i].incfgs : pt_pin[i].outcfgs);
}
}
void rkx110_linktx_wait_link_ready(struct rk_serdes *serdes, u8 id)
{
struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
u32 val;
int ret;
int sta;
if (id)
sta = SER_PCS1_READY;
else
sta = SER_PCS0_READY;
ret = read_poll_timeout(serdes->i2c_read_reg, ret,
!(ret < 0) && (val & sta),
1000, USEC_PER_SEC, false, client,
SER_GRF_SOC_STATUS0, &val);
if (ret < 0)
dev_err(&client->dev, "wait link ready timeout: 0x%08x\n", val);
else
dev_info(&client->dev, "link success: 0x%08x\n", val);
}
void rkx110_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
u8 pcs_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
u32 val;
serdes->i2c_read_reg(client, SER_PMA_STATUS(pcs_id), &val);
if (val & SER_PMA_FORCE_INIT_STA)
serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_INIT_CNT_CLR_MASK,
SER_PMA_INIT_CNT_CLR);
if (pll->force_init_en)
serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_FORCE_INIT_MASK,
SER_PMA_FORCE_INIT_EN);
else
serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_FORCE_INIT_MASK,
SER_PMA_FORCE_INIT_DISABLE);
if (pll->rate_mode == FDR_RATE_MODE)
serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
PMA_FDR_MODE);
else if (pll->rate_mode == HDR_RATE_MODE)
serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
PMA_HDR_MODE);
else
serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
PMA_QDR_MODE);
serdes->i2c_update_bits(client, SER_PMA_LOAD04(pcs_id), PMA_PLL_DIV_MASK,
PMA_PLL_DIV(pll->pll_div));
serdes->i2c_update_bits(client, SER_PMA_LOAD05(pcs_id), PMA_PLL_REFCLK_DIV_MASK,
PMA_PLL_REFCLK_DIV(pll->pll_refclk_div));
if (pll->pll_fck_vco_div2)
serdes->i2c_update_bits(client, SER_PMA_LOAD08(pcs_id), PMA_FCK_VCO_MASK,
PMA_FCK_VCO_DIV2);
else
serdes->i2c_update_bits(client, SER_PMA_LOAD08(pcs_id), PMA_FCK_VCO_MASK,
PMA_FCK_VCO);
if (pll->pll_div4)
serdes->i2c_update_bits(client, SER_PMA_LOAD09(pcs_id), PMA_PLL_DIV4_MASK,
PMA_PLL_DIV4);
else
serdes->i2c_update_bits(client, SER_PMA_LOAD09(pcs_id), PMA_PLL_DIV4_MASK,
PMA_PLL_DIV8);
serdes->i2c_update_bits(client, SER_PMA_LOAD0A(pcs_id), PMA_CLK_8X_DIV_MASK,
PMA_CLK_8X_DIV(pll->clk_div));
}
void rkx110_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
dev_info(serdes->dev, "%s: %d\n", __func__, enable);
if (enable)
serdes->i2c_update_bits(client, PCS_REG00(pcs_id), PCS_EN_MASK, PCS_EN);
else
serdes->i2c_update_bits(client, PCS_REG00(pcs_id), PCS_EN_MASK, PCS_DISABLE);
}
void rkx110_ser_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
u32 mask, val;
if (pma_id) {
mask = PMA1_EN_MASK;
val = enable ? PMA1_EN : PMA1_DISABLE;
} else {
mask = PMA0_EN_MASK;
val = enable ? PMA0_EN : PMA0_DISABLE;
}
serdes->i2c_update_bits(client, SER_GRF_SOC_CON7, mask, val);
}

View File

@@ -0,0 +1,515 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Zhang Yubing <yubing.zhang@rock-chips.com>
*/
#ifndef _RKX110_REG_H
#define _RKX110_REG_H
#include <linux/bits.h>
#define HIWORD_MASK(h, l) (GENMASK(h, l) | GENMASK(h, l) << 16)
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
#define HIWORD_UPDATE(v, m, l) (((v) << (l)) | (m << 16))
/************** RKX110 SER TX ***************/
#define RKX110_SER_CRU_BASE 0x00000000
#define RKX110_SER_GRF_BASE 0x00010000
#define GRF_REG(x) ((x) + RKX110_SER_GRF_BASE)
#define SER_GRF_GPIO0A_IOMUX_L GRF_REG(0x0)
#define SER_GRF_GPIO0A_IOMUX_H GRF_REG(0x4)
#define SER_GRF_GPIO0B_IOMUX_L GRF_REG(0x8)
#define SER_GRF_GPIO0B_IOMUX_H GRF_REG(0xC)
#define SER_GRF_GPIO0C_IOMUX_L GRF_REG(0x10)
#define SER_GRF_GPIO0C_IOMUX_H GRF_REG(0x14)
#define SER_GRF_GPIO0A_PULL_EN GRF_REG(0x20)
#define SER_GRF_GPIO0B_PULL_EN GRF_REG(0x24)
#define SER_GRF_GPIO0C_PULL_EN GRF_REG(0x28)
#define SER_GRF_GPIO1A_IOMUX GRF_REG(0x80)
#define SER_GRF_GPIO1B_IOMUX GRF_REG(0x84)
#define SER_GRF_GPIO1C_IOMUX GRF_REG(0x88)
#define SER_GRF_GPIO1A_PULL_CFG GRF_REG(0x90)
#define SER_GRF_GPIO1B_PULL_CFG GRF_REG(0x94)
#define SER_GRF_GPIO1C_PULL_CFG GRF_REG(0x98)
enum {
/* GPIO0A_IOMUX_H */
GPIO0A7_SHIFT = 12,
GPIO0A7_MASK = GENMASK(14, 12),
GPIO0A7_GPIO = 0,
GPIO0A7_SPI_MISO_M,
GPIO0A7_SPI_MISO_S,
GPIO0A7_UART1_TX_M,
GPIO0A7_UART1_TX_S,
GPIO0A7_GPO_2,
GPIO0A7_GPI_2,
GPIO0A7_TP15,
GPIO0A6_SHIFT = 8,
GPIO0A6_MASK = GENMASK(10, 8),
GPIO0A6_GPIO = 0,
GPIO0A6_SPI_MOSI_M,
GPIO0A6_SPI_MOSI_S,
GPIO0A6_UART0_RX_M,
GPIO0A6_UART0_RX_S,
GPIO0A6_GPO_1,
GPIO0A6_GPI_1,
GPIO0A6_I2C_DEBUG_SDA,
GPIO0A5_SHIFT = 4,
GPIO0A5_MASK = GENMASK(6, 4),
GPIO0A5_GPIO = 0,
GPIO0A5_SPI_CLK_M,
GPIO0A5_SPI_CLK_S,
GPIO0A5_UART0_TX_M,
GPIO0A5_UART0_TX_S,
GPIO0A5_GPO_0,
GPIO0A5_GPI_0,
GPIO0A5_I2C_DEBUG_SCL,
GPIO0A4_SHIFT = 0,
GPIO0A4_MASK = GENMASK(2, 0),
GPIO0A4_GPIO = 0,
GPIO0A4_INT_RX,
GPIO0A4_INT_TX,
/* GPIO0B_IOMUX_L */
GPIO0B3_SHIFT = 12,
GPIO0B3_MASK = GENMASK(14, 12),
GPIO0B3_GPIO = 0,
GPIO0B3_I2S_SDI0,
GPIO0B3_GPI_4,
GPIO0B3_GPO_4,
GPIO0B3_TP2,
GPIO0B2_SHIFT = 8,
GPIO0B2_MASK = GENMASK(10, 8),
GPIO0B2_GPIO = 0,
GPIO0B2_I2S_LRCK_M,
GPIO0B2_I2S_LRCK_S,
GPIO0B2_TP1,
GPIO0B1_SHIFT = 4,
GPIO0B1_MASK = GENMASK(6, 4),
GPIO0B1_GPIO = 0,
GPIO0B1_I2S_SCLK_M,
GPIO0B1_I2S_SCLK_S,
GPIO0B1_TP0,
GPIO0B0_SHIFT = 0,
GPIO0B0_MASK = GENMASK(2, 0),
GPIO0B0_GPIO = 0,
GPIO0B0_SPI_CSN_M,
GPIO0B0_SPI_CSN_S,
GPIO0B0_UART1_RX_M,
GPIO0B0_UART1_RX_S,
GPIO0B0_GPO_3,
GPIO0B0_GPI_3,
GPIO0B0_TP16,
/* GPIO0B_IOMUX_H */
GPIO0B7_SHIFT = 12,
GPIO0B7_MASK = GENMASK(14, 12),
GPIO0B7_GPIO = 0,
GPIO0B7_I2S_MCLK,
GPIO0B7_TEST_CLK_OUT,
GPIO0B7_MIPI_MCLK0,
GPIO0B7_TP6,
GPIO0B6_SHIFT = 8,
GPIO0B6_MASK = GENMASK(10, 8),
GPIO0B6_GPIO = 0,
GPIO0B6_I2S_SDI3,
GPIO0B6_I2S_SDO0,
GPIO0B6_TP5,
GPIO0B5_SHIFT = 4,
GPIO0B5_MASK = GENMASK(6, 4),
GPIO0B5_GPIO = 0,
GPIO0B5_I2S_SDI2,
GPIO0B5_GPI_6,
GPIO0B5_GPO_6,
GPIO0B5_I2C1_SDA_M,
GPIO0B5_I2C1_SDA_S,
GPIO0B5_TP4,
GPIO0B4_SHIFT = 0,
GPIO0B4_MASK = GENMASK(2, 0),
GPIO0B4_GPIO = 0,
GPIO0B4_I2S_SDI1,
GPIO0B4_GPI_5,
GPIO0B4_GPO_5,
GPIO0B5_I2C1_SCL_M,
GPIO0B5_I2C1_SCL_S,
GPIO0B5_TP3,
/* GPIO0C_IOMUX_L */
GPIO0C4_SHIFT = 12,
GPIO0C4_MASK = GENMASK(14, 12),
GPIO0C4_GPIO = 0,
GPIO0C4_LCDC_D0,
GPIO0C4_CIF_D0,
GPIO0C4_TP11,
GPIO0C3_SHIFT = 9,
GPIO0C3_MASK = GENMASK(11, 9),
GPIO0C3_GPIO = 0,
GPIO0C3_LCDC_DEN,
GPIO0C3_CIF_CLK_OUT,
GPIO0C3_MIPI_CLK1,
GPIO0C3_TP10,
GPIO0C2_SHIFT = 6,
GPIO0C2_MASK = GENMASK(8, 6),
GPIO0C2_GPIO = 0,
GPIO0C2_LCDC_HSYNC,
GPIO0C2_CIF_HSYNC,
GPIO0C2_TP9,
GPIO0C1_SHIFT = 3,
GPIO0C1_MASK = GENMASK(5, 3),
GPIO0C1_GPIO = 0,
GPIO0C1_LCDC_VSYNC,
GPIO0C1_CIF_VSYNC,
GPIO0C1_TP8,
GPIO0C0_SHIFT = 0,
GPIO0C0_MASK = GENMASK(2, 0),
GPIO0C0_GPIO = 0,
GPIO0C0_LCDC_CLK,
GPIO0C0_CIF_CLK,
GPIO0C0_TP7,
/* GPIO0C_IOMUX_H */
GPIO0C7_SHIFT = 6,
GPIO0C7_MASK = GENMASK(8, 6),
GPIO0C7_GPIO = 0,
GPIO0C7_LCDC_D3,
GPIO0C7_CIF_D3,
GPIO0C7_TP14,
GPIO0C6_SHIFT = 3,
GPIO0C6_MASK = GENMASK(5, 3),
GPIO0C6_GPIO = 0,
GPIO0C6_LCDC_D2,
GPIO0C6_CIF_D2,
GPIO0C6_TP13,
GPIO0C5_SHIFT = 0,
GPIO0C5_MASK = GENMASK(2, 0),
GPIO0C5_GPIO = 0,
GPIO0C5_LCDC_D1,
GPIO0C5_CIF_D1,
GPIO0C5_TP12,
/* GPIO1A_IOMUX */
GPIO1A7_SHIFT = 14,
GPIO1A7_MASK = GENMASK(15, 14),
GPIO1A7_GPIO = 0,
GPIO1A7_LCDC_D11,
GPIO1A7_CIF_D11,
GPIO1A7_MIPI0_RX2_P,
GPIO1A6_SHIFT = 12,
GPIO1A6_MASK = GENMASK(13, 12),
GPIO1A6_GPIO = 0,
GPIO1A6_LCDC_D10,
GPIO1A6_CIF_D10,
GPIO1A6_MIPI0_RX2_N,
GPIO1A5_SHIFT = 10,
GPIO1A5_MASK = GENMASK(11, 10),
GPIO1A5_GPIO = 0,
GPIO1A5_LCDC_D9,
GPIO1A5_CIF_D9,
GPIO1A5_MIPI0_TCK_P,
GPIO1A4_SHIFT = 8,
GPIO1A4_MASK = GENMASK(9, 8),
GPIO1A4_GPIO = 0,
GPIO1A4_LCDC_D8,
GPIO1A4_CIF_D8,
GPIO1A4_MIPI0_TCK_N,
GPIO1A3_SHIFT = 6,
GPIO1A3_MASK = GENMASK(7, 6),
GPIO1A3_GPIO = 0,
GPIO1A3_LCDC_D7,
GPIO1A3_CIF_D7,
GPIO1A3_MIPI0_RX1_P,
GPIO1A2_SHIFT = 4,
GPIO1A2_MASK = GENMASK(5, 4),
GPIO1A2_GPIO = 0,
GPIO1A2_LCDC_D6,
GPIO1A2_CIF_D6,
GPIO1A2_MIPI0_RX1_N,
GPIO1A1_SHIFT = 2,
GPIO1A1_MASK = GENMASK(3, 2),
GPIO1A1_GPIO = 0,
GPIO1A1_LCDC_D5,
GPIO1A1_CIF_D5,
GPIO1A1_MIPI0_RX0_P,
GPIO1A0_SHIFT = 0,
GPIO1A0_MASK = GENMASK(1, 0),
GPIO1A0_GPIO = 0,
GPIO1A0_LCDC_D4,
GPIO1A0_CIF_D4,
GPIO1A0_MIPI0_RX0_N,
/* GPIO1B_IOMUX */
GPIO1B7_SHIFT = 14,
GPIO1B7_MASK = GENMASK(15, 14),
GPIO1B7_GPIO = 0,
GPIO1B7_LCDC_D19,
GPIO1B7_CIF_D19,
GPIO1B7_MIPI1_TCK_P,
GPIO1B6_SHIFT = 12,
GPIO1B6_MASK = GENMASK(13, 12),
GPIO1B6_GPIO = 0,
GPIO1B6_LCDC_D18,
GPIO1B6_CIF_D18,
GPIO1B6_MIPI1_TCK_N,
GPIO1B5_SHIFT = 10,
GPIO1B5_MASK = GENMASK(11, 10),
GPIO1B5_GPIO = 0,
GPIO1B5_LCDC_D17,
GPIO1B5_CIF_D17,
GPIO1B5_MIPI1_RX1_P,
GPIO1B4_SHIFT = 8,
GPIO1B4_MASK = GENMASK(9, 8),
GPIO1B4_GPIO = 0,
GPIO1B4_LCDC_D16,
GPIO1B4_CIF_D16,
GPIO1B4_MIPI1_RX1_N,
GPIO1B3_SHIFT = 6,
GPIO1B3_MASK = GENMASK(7, 6),
GPIO1B3_GPIO = 0,
GPIO1B3_LCDC_D15,
GPIO1B3_CIF_D15,
GPIO1B3_MIPI1_RX0_P,
GPIO1B2_SHIFT = 4,
GPIO1B2_MASK = GENMASK(5, 4),
GPIO1B2_GPIO = 0,
GPIO1B2_LCDC_D14,
GPIO1B2_CIF_D14,
GPIO1B2_MIPI1_RX0_N,
GPIO1B1_SHIFT = 2,
GPIO1B1_MASK = GENMASK(3, 2),
GPIO1B1_GPIO = 0,
GPIO1B1_LCDC_D13,
GPIO1B1_CIF_D13,
GPIO1B1_MIPI0_RX3_P,
GPIO1B0_SHIFT = 0,
GPIO1B0_MASK = GENMASK(1, 0),
GPIO1B0_GPIO = 0,
GPIO1B0_LCDC_D12,
GPIO1B0_CIF_D12,
GPIO1B0_MIPI0_RX3_N,
/* GPIO1C_IOMUX */
GPIO1C3_SHIFT = 6,
GPIO1C3_MASK = GENMASK(7, 6),
GPIO1C3_GPIO = 0,
GPIO1C3_LCDC_D23,
GPIO1C3_CIF_D23,
GPIO1C3_MIPI1_RX3_P,
GPIO1C2_SHIFT = 4,
GPIO1C2_MASK = GENMASK(5, 4),
GPIO1C2_GPIO = 0,
GPIO1C2_LCDC_D22,
GPIO1C2_CIF_D22,
GPIO1C2_MIPI1_RX3_N,
GPIO1C1_SHIFT = 2,
GPIO1C1_MASK = GENMASK(3, 2),
GPIO1C1_GPIO = 0,
GPIO1C1_LCDC_D21,
GPIO1C1_CIF_D21,
GPIO1C1_MIPI1_RX2_P,
GPIO1C0_SHIFT = 0,
GPIO1C0_MASK = GENMASK(1, 0),
GPIO1C0_GPIO = 0,
GPIO1C0_LCDC_D20,
GPIO1C0_CIF_D20,
GPIO1C0_MIPI1_RX2_N,
};
#define SER_GRF_SOC_CON0 GRF_REG(0x100)
#define SER_GRF_SOC_CON1 GRF_REG(0x104)
#define SER_GRF_SOC_CON2 GRF_REG(0x108)
#define SER_GRF_SOC_CON3 GRF_REG(0x10C)
#define SER_GRF_SOC_CON4 GRF_REG(0x110)
#define SER_GRF_SOC_CON5 GRF_REG(0x114)
#define SER_GRF_SOC_CON6 GRF_REG(0x118)
#define SER_GRF_SOC_CON7 GRF_REG(0x11C)
#define SER_GRF_SOC_STATUS0 GRF_REG(0x160)
enum {
/* SOC_CON0 */
LVDS_ALIGN_MODE_SHIFT = 13,
LVDS_ALIGN_MODE_MASK = GENMASK(14, 13),
LVDS_ALIGN_8BIT = 0,
LVDS_ALIGN_10BIT = 0,
LVDS_ALIGN_12BIT = 0,
LVDS_ALIGN_EN_SHIFT = 12,
LVDS_ALIGN_EN_MASK = GENMASK(12, 12),
LVDS_ALIGN_DISABLE = 0,
LVDS_ALIGN_EN,
/* SOC_CON2 */
LVDS1_MSB_SHIFT = 5,
LVDS1_MSB_MASK = GENMASK(5, 5),
LVDS_LSB = 0,
LVDS_MSB,
LVDS1_FORMAT_SHIFT = 3,
LVDS1_FORMAT_MASK = GENMASK(4, 3),
LVDS_FORMAT_VESA_24BIT = 0,
LVDS_FORMAT_JEIDA_24BIT,
LVDS_FORMAT_JEIDA_18BIT,
LVDS_FORMAT_VESA_18BIT,
LVDS0_MSB_SHIFT = 2,
LVDS0_MSB_MASK = GENMASK(2, 2),
LVDS0_FORMAT_SHIFT = 0,
LVDS0_FORMAT_MASK = GENMASK(1, 0),
/* SOC_CON3 */
AUDIO_PCS_SEL_SHIFT = 15,
AUDIO_PCS_SEL_MASK = GENMASK(15, 15),
AUDIO_SEL_PCS0 = 0,
AUDIO_SEL_PCS1 = 1,
CMD_PCS_SEL_SHIFT = 14,
CMD_PCS_SEL_MASK = GENMASK(14, 14),
CMD_SEL_PCS0 = 0,
CMD_SEL_PCS1 = 1,
/* SOC_CON4 */
LVDS1_LINK_SEL_SHIFT = 15,
LVDS1_LINK_SEL_MASK = GENMASK(15, 15),
/* enable lvds source from pattern generation */
LINK_SEL_PG_DISABLE = 0,
LINK_SEL_PG_EN = 1,
LVDS0_LINK_SEL_SHIFT = 14,
LVDS0_LINK_SEL_MASK = GENMASK(14, 14),
DSI1_LINK_SEL_SHIFT = 13,
DSI1_LINK_SEL_MASK = GENMASK(13, 13),
DSI0_LINK_SEL_SHIFT = 12,
DSI0_LINK_SEL_MASK = GENMASK(12, 12),
RGB_DCLK_BYPASS_SHIFT = 9,
RGB_DCLK_BYPASS_MASK = GENMASK(9, 9),
RGB_DCLK_DCLK_DLY_SHIFT = 1,
RGB_DCLK_DCLK_DLY_MASK = GENMASK(8, 1),
RGB_DCLK_INV_SHIFT = 0,
RGB_DCLK_INV_MASK = GENMASK(0, 0),
/* SOC_CON5 */
LDO_PLC_SEL_SHIFT = 8,
LDO_PLC_SEL_MASK = GENMASK(8, 8),
LDO_PLC_170 = 0,
LDO_PLC_270,
LDO_VOL_SEL_SHIFT = 4,
LDO_VOL_SEL_MASK = HIWORD_MASK(7, 4),
LDO_VOL_110 = HIWORD_UPDATE(0, GENMASK(7, 4), 4),
LDO_VOL_115 = HIWORD_UPDATE(1, GENMASK(7, 4), 4),
LDO_VOL_120 = HIWORD_UPDATE(2, GENMASK(7, 4), 4),
LDO_VOL_125 = HIWORD_UPDATE(3, GENMASK(7, 4), 4),
LDO_VOL_130 = HIWORD_UPDATE(4, GENMASK(7, 4), 4),
LDO_VOL_135 = HIWORD_UPDATE(5, GENMASK(7, 4), 4),
LDO_VOL_140 = HIWORD_UPDATE(6, GENMASK(7, 4), 4),
LDO_VOL_145 = HIWORD_UPDATE(4, GENMASK(7, 4), 4),
LDO_BG_TRIM_SHIFT = 4,
LDO_BG_TRIM_MASK = GENMASK(7, 4),
LDO_BG_TRIM_OUT = 0,
LDO_BG_TRIM_OUT_55_N = 0,
LDO_BG_TRIM_OUT_110_N,
/* SOC_CON7 */
PMA_PLL_CTRL_SEL_SHIFT = 15,
PMA_PLL_CTRL_SEL_MASK = GENMASK(15, 15),
PMA_PLL_CTRL_BY_PMA0 = 0,
PMA_PLL_CTRL_BY_PMA1,
PMA1_EN_SHIFT = 9,
PMA1_EN_MASK = HIWORD_MASK(9, 9),
PMA1_EN = HIWORD_UPDATE(1, BIT(9), 9),
PMA1_DISABLE = HIWORD_UPDATE(0, BIT(9), 9),
PMA0_EN_SHIFT = 8,
PMA0_EN_MASK = HIWORD_MASK(8, 8),
PMA0_EN = HIWORD_UPDATE(1, BIT(8), 8),
PMA0_DISABLE = HIWORD_UPDATE(0, BIT(8), 8),
/* SER_GRF_IRQ_EN */
/* SER_GRF_SOC_STATUS0 */
SER_PCS1_READY = BIT(21),
SER_PCS0_READY = BIT(20),
};
#define RKX110_CSI2HOST0_BASE 0x00020000
#define RKX110_CSI2HOST1_BASE 0x00028000
#define CSI2HOST_N_LANES 0x0004
#define CSI2HOST_RESETN 0x0010
#define CSI2HOST_CONTROL 0x0040
#define SW_DATATYPE_LE(x) UPDATE(x, 31, 26)
#define SW_DATETYPE_LS(x) UPDATE(x, 25, 20)
#define SW_DATETYPE_FE_MASK GENMASK(19, 14)
#define SW_DATETYPE_FE(x) UPDATE(x, 19, 14)
#define SW_DATETYPE_FS_MASK GENMASK(13, 8)
#define SW_DATETYPE_FS(x) UPDATE(x, 13, 8)
#define SW_DSI_EN BIT(4)
#define RKX110_VICAP_BASE 0x00030000
#define RKX110_GPIO0_BASE 0x00040000
#define RKX110_DSI_RX0_BASE 0x00060000
#define RKX110_DSI_RX1_BASE 0x00068000
#define RKX110_SER_RKLINK_BASE 0x00070000
#define RKX110_SER_PCS0_BASE 0x00074000
#define RKX110_SER_PCS1_BASE 0x00075000
#define RKX110_SER_PCS_OFFSET 0x00001000
#define RKX110_EFUSE_BASE 0x00800000
#define RKX110_MIPI_LVDS_RX_PHY0_BASE 0x00090000
#define RKX110_GRF_MIPI0_BASE 0x000A0000
#define RKX110_MIPI_LVDS_RX_PHY1_BASE 0x000B0000
#define RKX110_GRF_MIPI1_BASE 0x000C0000
#define RKX110_SER_PMA0_BASE 0x000D0000
#define RKX110_SER_PMA1_BASE 0x000E0000
#define RKX110_SER_PMA_OFFSET 0x00010000
#define RKX110_GPIO1_BASE 0x000F0000
#define RKX110_PATTERN_GEN_DSI0_BASE 0x00100000
#define RKX110_PATTERN_GEN_DSI1_BASE 0x00110000
#define RKX110_PATTERN_GEN_LVDS0_BASE 0x00120000
#define RKX110_PATTERN_GEN_LVDS1_BASE 0x00130000
#endif

View File

@@ -0,0 +1,366 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Zhang Yubing <yubing.zhang@rock-chips.com>
*/
#ifndef _RKX110_X120_H
#define _RKX110_X120_H
#include <drm/drm_panel.h>
#include <dt-bindings/mfd/rockchip-serdes.h>
#include <linux/i2c.h>
#include <video/videomode.h>
#define MAX_PANEL 2
#define RK_SERDES_PASSTHROUGH_CNT 11
#define SERDES_VERSION_V0(type) 0x2201
#define SERDES_VERSION_V1(type) (type ? 0x1200001 : 0x1100001)
#define SER_GRF_CHIP_ID 0x10800
#define DES_GRF_CHIP_ID 0x1010400
#define HIWORD_MASK(h, l) (GENMASK(h, l) | GENMASK(h, l) << 16)
enum {
SERDES_V0 = 0,
SERDES_V1,
};
enum {
LOCAL_MODE = 0,
REMOTE_MODE,
};
enum {
STREAM_DISPLAY = 0,
STREAM_CAMERA,
};
enum {
DEVICE_LOCAL = 0,
DEVICE_REMOTE0,
DEVICE_REMOTE1,
DEVICE_MAX,
};
enum {
PORT_REMOTE0,
PORT_REMOTE1,
PORT_REMOTE_MAX,
};
enum {
LINK_LANE0,
LINK_LANE1,
LINK_LANE_DUAL,
};
enum combtx_phy_mode {
COMBTX_PHY_MODE_GPIO,
COMBTX_PHY_MODE_VIDEO_LVDS,
COMBTX_PHY_MODE_VIDEO_MIPI,
COMBTX_PHY_MODE_VIDEO_MINI_LVDS,
};
enum comb_phy_id {
COMBPHY_0,
COMBPHY_1,
COMBPHY_MAX,
};
enum combrx_phy_mode {
COMBRX_PHY_MODE_RGB,
COMBRX_PHY_MODE_VIDEO_LVDS,
COMBRX_PHY_MODE_VIDEO_MIPI,
COMBRX_PHY_MODE_LVDS_CAMERA,
};
enum serdes_dsi_mode_flags {
SERDES_MIPI_DSI_MODE_VIDEO = 1,
SERDES_MIPI_DSI_MODE_VIDEO_BURST = 2,
SERDES_MIPI_DSI_MODE_VIDEO_SYNC_PULSE = 4,
SERDES_MIPI_DSI_MODE_VIDEO_HFP = 8,
SERDES_MIPI_DSI_MODE_VIDEO_HBP = 16,
SERDES_MIPI_DSI_MODE_EOT_PACKET = 32,
SERDES_MIPI_DSI_CLOCK_NON_CONTINUOUS = 64,
SERDES_MIPI_DSI_MODE_LPM = 128,
};
enum serdes_dsi_bus_format {
SERDES_MIPI_DSI_FMT_RGB888,
SERDES_MIPI_DSI_FMT_RGB666,
SERDES_MIPI_DSI_FMT_RGB666_PACKED,
SERDES_MIPI_DSI_FMT_RGB565,
};
enum serdes_frame_mode {
SERDES_FRAME_NORMAL_MODE,
SERDES_SP_PIXEL_INTERLEAVED,
SERDES_SP_LEFT_RIGHT_SPLIT,
SERDES_SP_LINE_INTERLEAVED,
};
struct configure_opts_combphy {
unsigned int clk_miss;
unsigned int clk_post;
unsigned int clk_pre;
unsigned int clk_prepare;
unsigned int clk_settle;
unsigned int clk_term_en;
unsigned int clk_trail;
unsigned int clk_zero;
unsigned int d_term_en;
unsigned int eot;
unsigned int hs_exit;
unsigned int hs_prepare;
unsigned int hs_settle;
unsigned int hs_skip;
unsigned int hs_trail;
unsigned int hs_zero;
unsigned int init;
unsigned int lpx;
unsigned int ta_get;
unsigned int ta_go;
unsigned int ta_sure;
unsigned int wakeup;
unsigned long hs_clk_rate;
unsigned long lp_clk_rate;
unsigned char lanes;
};
struct rkx120_combtxphy {
enum combtx_phy_mode mode;
unsigned int flags;
u8 ref_div;
u16 fb_div;
u8 rate_factor;
u64 rate;
struct configure_opts_combphy mipi_dphy_cfg;
};
struct rkx110_combrxphy {
enum combrx_phy_mode mode;
u64 rate;
struct configure_opts_combphy mipi_dphy_cfg;
};
struct rkx120_dsi_tx {
int bpp; /* 24/18/16*/
enum serdes_dsi_bus_format bus_format;
enum serdes_dsi_mode_flags mode_flags;
struct videomode *vm;
uint8_t channel;
uint8_t lanes;
};
struct rkx110_dsi_rx {
enum serdes_dsi_mode_flags mode_flags;
struct videomode *vm;
uint8_t channel;
uint8_t lanes;
};
enum {
OUTPUT,
INPUT,
};
enum rk_serdes_rate {
RATE_2GBPS_83M,
RATE_4GBPS_83M,
RATE_4GBPS_125M,
RATE_4GBPS_250M,
RATE_4_5GBPS_140M,
RATE_4_8GBPS_150M,
RATE_5GBPS_156M,
RATE_6GBPS_187M,
};
enum {
FDR_RATE_MODE,
HDR_RATE_MODE,
QDR_RATE_MODE,
};
enum rk_serdes_route_type {
ROUTE_MULTI_SOURCE = BIT(0),
ROUTE_MULTI_LANE = BIT(1),
ROUTE_MULTI_CHANNEL = BIT(2),
ROUTE_MULTI_REMOTE = BIT(3),
ROUTE_MULTI_DSI_INPUT = BIT(20),
ROUTE_MULTI_LVDS_INPUT = BIT(21),
ROUTE_MULTI_MIRROR = BIT(22),
ROUTE_MULTI_SPLIT = BIT(23),
};
struct rk_serdes_pma_pll {
uint32_t rate_mode;
uint32_t pll_refclk_div;
uint32_t pll_div;
uint32_t clk_div;
bool pll_div4;
bool pll_fck_vco_div2;
bool force_init_en;
};
struct rk_serdes_reg {
const char *name;
uint32_t reg_base;
uint32_t reg_len;
};
struct rk_serdes_route {
u32 stream_type;
struct videomode vm;
enum serdes_frame_mode frame_mode;
u32 local_port0;
u32 local_port1;
u32 remote0_port0;
u32 remote0_port1;
u32 remote1_port0;
u32 remote1_port1;
};
struct rk_serdes_chip {
bool is_remote;
struct i2c_client *client;
struct hwclk *hwclk;
struct rk_serdes *serdes;
};
struct pattern_gen {
const char *name;
struct rk_serdes_chip *chip;
u32 base;
u32 link_src_reg;
u8 link_src_offset;
};
struct rk_serdes_pt_pin {
u32 bank;
u32 pin;
u32 incfgs;
u32 outcfgs;
};
struct rk_serdes_pt {
u32 en_reg;
u32 en_mask;
u32 en_val;
u32 dir_reg;
u32 dir_mask;
u32 dir_val;
int configs;
struct rk_serdes_pt_pin pt_pins[4];
};
struct rk_serdes {
struct device *dev;
struct rk_serdes_chip chip[DEVICE_MAX];
struct gpio_desc *reset;
struct gpio_desc *enable;
/*
* Control by I2C-Debug
*/
bool rkx110_debug;
bool rkx120_debug;
enum rk_serdes_rate rate;
struct dentry *debugfs_root;
struct dentry *debugfs_local;
struct dentry *debugfs_remote0;
struct dentry *debugfs_remote1;
struct dentry *debugfs_rate;
struct videomode *vm;
u32 stream_type;
u32 version;
u32 route_flag;
u8 remote_nr;
struct rkx110_combrxphy combrxphy;
struct rkx110_dsi_rx dsi_rx;
struct rkx120_combtxphy combtxphy;
struct rkx120_dsi_tx dsi_tx;
int (*i2c_read_reg)(struct i2c_client *client, u32 addr, u32 *value);
int (*i2c_write_reg)(struct i2c_client *client, u32 addr, u32 value);
int (*i2c_update_bits)(struct i2c_client *client, u32 reg, u32 mask, u32 val);
int (*route_prepare)(struct rk_serdes *serdes, struct rk_serdes_route *route);
int (*route_enable)(struct rk_serdes *serdes, struct rk_serdes_route *route);
int (*route_disable)(struct rk_serdes *serdes, struct rk_serdes_route *route);
int (*route_unprepare)(struct rk_serdes *serdes, struct rk_serdes_route *route);
int (*set_hwpin)(struct rk_serdes *serdes, struct i2c_client *client,
int pintype, int bank, uint32_t mpins, uint32_t param);
};
struct cmd_ctrl_hdr {
u8 dtype; /* data type */
u8 wait; /* ms */
u8 dlen; /* payload len */
} __packed;
struct cmd_desc {
struct cmd_ctrl_hdr dchdr;
u8 *payload;
};
struct panel_cmds {
u8 *buf;
int blen;
struct cmd_desc *cmds;
int cmd_cnt;
};
struct rk_serdes_panel {
struct drm_panel panel;
struct device *dev;
struct rk_serdes *parent;
struct rk_serdes_route route;
unsigned int bus_format;
int link_mode;
struct panel_cmds *on_cmds;
struct panel_cmds *off_cmds;
struct regulator *supply;
struct gpio_desc *enable_gpio;
struct gpio_desc *reset_gpio;
};
int rkx110_linktx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route);
void rkx110_linktx_video_enable(struct rk_serdes *serdes, u8 dev_id, bool enable);
void rkx110_linktx_channel_enable(struct rk_serdes *serdes, u8 ch_id, u8 dev_id, bool enable);
void rkx120_linkrx_engine_enable(struct rk_serdes *serdes, u8 en_id, u8 dev_id, bool enable);
void rkx110_set_stream_source(struct rk_serdes *serdes, int local_port, u8 dev_id);
int rkx120_linkrx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id);
int rkx120_rgb_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id);
int rkx120_lvds_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
u8 phy_id);
void rkx120_linkrx_gpi_gpo_mux_cfg(struct rk_serdes *serdes, u32 mux, u8 remote_id);
void rkx110_linktx_gpi_gpo_mux_cfg(struct rk_serdes *serdes, u32 mux, u8 remote_id);
int rkx110_rgb_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route);
int rkx110_lvds_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, int id);
void rkx110_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry);
void rkx120_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry);
void rkx110_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
u8 pcs_id, u8 dev_id);
void rkx120_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
u8 pcs_id, u8 dev_id);
void rkx110_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id);
void rkx120_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id);
void rkx110_ser_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 remote_id);
void rkx120_des_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 remote_id);
void rkx110_linktx_wait_link_ready(struct rk_serdes *serdes, u8 id);
void rkx120_linkrx_wait_link_ready(struct rk_serdes *serdes, u8 id);
void rkx110_x120_pattern_gen_debugfs_create_file(struct pattern_gen *pattern_gen,
struct rk_serdes_chip *chip,
struct dentry *dentry);
void rkx110_linktx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
bool is_rx);
void rkx120_linkrx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
bool is_rx);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,642 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* rockchip SerDes Panele driver for drm platform
*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include "rkx110_x120.h"
#include "rkx120_dsi_tx.h"
static inline struct rk_serdes_panel *to_serdes_panel(struct drm_panel *panel)
{
return container_of(panel, struct rk_serdes_panel, panel);
}
static int serdes_panel_prepare(struct drm_panel *panel)
{
struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
struct rk_serdes_route *route = &sd_panel->route;
struct rk_serdes *serdes = sd_panel->parent;
if (sd_panel->supply) {
int err;
err = regulator_enable(sd_panel->supply);
if (err < 0) {
dev_err(sd_panel->dev, "failed to enable supply: %d\n",
err);
return err;
}
mdelay(20);
}
if (sd_panel->enable_gpio) {
gpiod_set_value_cansleep(sd_panel->enable_gpio, 1);
mdelay(20);
}
if (sd_panel->reset_gpio) {
gpiod_set_value_cansleep(sd_panel->reset_gpio, 1);
mdelay(20);
}
if (serdes->route_prepare)
serdes->route_prepare(serdes, &sd_panel->route);
if (route->remote0_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE0,
sd_panel->on_cmds);
if (route->remote1_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE1,
sd_panel->on_cmds);
return 0;
}
static int serdes_panel_enable(struct drm_panel *panel)
{
struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
struct rk_serdes *serdes = sd_panel->parent;
if (serdes->route_enable)
serdes->route_enable(serdes, &sd_panel->route);
return 0;
}
static int serdes_panel_disable(struct drm_panel *panel)
{
struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
struct rk_serdes *serdes = sd_panel->parent;
if (serdes->route_disable)
serdes->route_disable(serdes, &sd_panel->route);
return 0;
}
static int serdes_panel_unprepare(struct drm_panel *panel)
{
struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
struct rk_serdes_route *route = &sd_panel->route;
struct rk_serdes *serdes = sd_panel->parent;
if (route->remote0_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE0,
sd_panel->off_cmds);
if (route->remote1_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE1,
sd_panel->off_cmds);
if (serdes->route_unprepare)
serdes->route_unprepare(serdes, &sd_panel->route);
if (sd_panel->reset_gpio) {
gpiod_set_value_cansleep(sd_panel->reset_gpio, 0);
mdelay(20);
}
if (sd_panel->enable_gpio) {
gpiod_set_value_cansleep(sd_panel->enable_gpio, 0);
mdelay(20);
}
if (sd_panel->supply)
regulator_disable(sd_panel->supply);
return 0;
}
static int serdes_panel_of_get_native_mode(struct rk_serdes_panel *sd_panel,
struct drm_connector *connector)
{
struct rk_serdes_route *route = &sd_panel->route;
struct device_node *timings_np;
struct drm_display_mode *mode;
struct drm_device *drm = connector->dev;
u32 bus_flags;
int ret;
timings_np = of_get_child_by_name(sd_panel->dev->of_node, "display-timings");
if (!timings_np) {
dev_err(sd_panel->dev, "failed to find display-timings node\n");
return 0;
}
of_node_put(timings_np);
mode = drm_mode_create(drm);
if (!mode)
return 0;
ret = of_get_drm_display_mode(sd_panel->dev->of_node, mode,
&bus_flags, OF_USE_NATIVE_MODE);
if (ret) {
dev_err(sd_panel->dev, "failed to find dts display timings\n");
drm_mode_destroy(drm, mode);
return 0;
}
drm_mode_set_name(mode);
connector->display_info.bus_flags = bus_flags;
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
drm_display_mode_to_videomode(mode, &sd_panel->route.vm);
if (route->frame_mode == SERDES_SP_LEFT_RIGHT_SPLIT ||
route->frame_mode == SERDES_SP_PIXEL_INTERLEAVED) {
sd_panel->route.vm.pixelclock /= 2;
sd_panel->route.vm.hactive /= 2;
sd_panel->route.vm.hfront_porch /= 2;
sd_panel->route.vm.hback_porch /= 2;
sd_panel->route.vm.hsync_len /= 2;
} else if (route->frame_mode == SERDES_SP_LINE_INTERLEAVED) {
sd_panel->route.vm.pixelclock /= 2;
sd_panel->route.vm.vactive /= 2;
sd_panel->route.vm.vfront_porch /= 2;
sd_panel->route.vm.vback_porch /= 2;
sd_panel->route.vm.vsync_len /= 2;
}
return 1;
}
static int serdes_panel_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
int num = 0;
num += serdes_panel_of_get_native_mode(sd_panel, connector);
drm_display_info_set_bus_formats(&connector->display_info,
&sd_panel->bus_format, 1);
return num;
}
static const struct drm_panel_funcs serdes_panel_funcs = {
.prepare = serdes_panel_prepare,
.enable = serdes_panel_enable,
.disable = serdes_panel_disable,
.unprepare = serdes_panel_unprepare,
.get_modes = serdes_panel_get_modes,
};
static int
dsi_panel_parse_cmds(struct device *dev, const u8 *data,
int blen, struct panel_cmds *pcmds)
{
unsigned int len;
char *buf, *bp;
struct cmd_ctrl_hdr *dchdr;
int i, cnt;
if (!pcmds)
return -EINVAL;
buf = devm_kmemdup(dev, data, blen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* scan init commands */
bp = buf;
len = blen;
cnt = 0;
while (len > sizeof(*dchdr)) {
dchdr = (struct cmd_ctrl_hdr *)bp;
if (dchdr->dlen > len) {
dev_err(dev, "%s: error, len=%d", __func__, dchdr->dlen);
return -EINVAL;
}
bp += sizeof(*dchdr);
len -= sizeof(*dchdr);
bp += dchdr->dlen;
len -= dchdr->dlen;
cnt++;
}
if (len != 0) {
dev_err(dev, "%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen);
return -EINVAL;
}
pcmds->cmds = devm_kcalloc(dev, cnt, sizeof(struct cmd_desc), GFP_KERNEL);
if (!pcmds->cmds)
return -ENOMEM;
pcmds->cmd_cnt = cnt;
pcmds->buf = buf;
pcmds->blen = blen;
bp = buf;
len = blen;
for (i = 0; i < cnt; i++) {
dchdr = (struct cmd_ctrl_hdr *)bp;
len -= sizeof(*dchdr);
bp += sizeof(*dchdr);
pcmds->cmds[i].dchdr = *dchdr;
pcmds->cmds[i].payload = bp;
bp += dchdr->dlen;
len -= dchdr->dlen;
}
return 0;
}
static int serdes_dsi_panel_get_cmds(struct rk_serdes_panel *sd_panel)
{
struct device_node *np = sd_panel->dev->of_node;
struct device *dev = sd_panel->dev;
const void *data;
int len, err;
data = of_get_property(np, "panel-init-sequence", &len);
if (data) {
sd_panel->on_cmds = devm_kzalloc(dev, sizeof(*sd_panel->on_cmds), GFP_KERNEL);
if (!sd_panel->on_cmds)
return -ENOMEM;
err = dsi_panel_parse_cmds(dev, data, len, sd_panel->on_cmds);
if (err) {
dev_err(dev, "failed to parse dsi panel init sequence\n");
return err;
}
}
data = of_get_property(np, "panel-exit-sequence", &len);
if (data) {
sd_panel->off_cmds = devm_kzalloc(dev, sizeof(*sd_panel->off_cmds), GFP_KERNEL);
if (!sd_panel->off_cmds)
return -ENOMEM;
err = dsi_panel_parse_cmds(dev, data, len, sd_panel->off_cmds);
if (err) {
dev_err(dev, "failed to parse dsi panel exit sequence\n");
return err;
}
}
return 0;
}
static struct mipi_dsi_device *serdes_attach_dsi(struct rk_serdes_panel *sd_panel,
struct device_node *dsi_node)
{
const struct mipi_dsi_device_info info = { "serdes", 0, NULL };
struct mipi_dsi_device *dsi;
struct mipi_dsi_host *host;
int ret;
host = of_find_mipi_dsi_host_by_node(dsi_node);
if (!host) {
dev_err(sd_panel->dev, "failed to find dsi host\n");
return ERR_PTR(-EPROBE_DEFER);
}
dsi = mipi_dsi_device_register_full(host, &info);
if (IS_ERR(dsi)) {
dev_err(sd_panel->dev, "failed to create dsi device\n");
return dsi;
}
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(sd_panel->dev, "failed to attach dsi to host\n");
mipi_dsi_device_unregister(dsi);
return ERR_PTR(ret);
}
return dsi;
}
static int rkx120_dsi_rx_parse(struct rk_serdes_panel *sd_panel)
{
struct device_node *np = sd_panel->dev->of_node;
struct rk_serdes *serdes = sd_panel->parent;
struct rkx110_dsi_rx *dsi_rx = &serdes->dsi_rx;
struct mipi_dsi_device *dsi;
struct device_node *dsi_node;
u32 val;
if (of_property_read_u32(np, "dsi-rx,lanes", &val))
dsi_rx->lanes = 4;
else
dsi_rx->lanes = val;
if (of_property_read_bool(np, "dsi-rx,video-mode"))
dsi_rx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM | SERDES_MIPI_DSI_MODE_VIDEO |
SERDES_MIPI_DSI_MODE_VIDEO_BURST;
else
dsi_rx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM;
dsi_node = of_graph_get_remote_node(np, 0, -1);
if (!dsi_node)
dev_err(sd_panel->dev, "failed to get remote node for primary dsi\n");
dsi = serdes_attach_dsi(sd_panel, dsi_node);
if (IS_ERR(dsi))
return -EPROBE_DEFER;
return 0;
}
static int rkx120_dsi_tx_parse(struct rk_serdes_panel *sd_panel)
{
struct device_node *np = sd_panel->dev->of_node;
struct rk_serdes *serdes = sd_panel->parent;
struct rkx120_dsi_tx *dsi_tx = &serdes->dsi_tx;
const char *string;
int ret;
u32 val;
if (of_property_read_u32(np, "dsi-tx,lanes", &val))
dsi_tx->lanes = 4;
else
dsi_tx->lanes = val;
if (of_property_read_bool(np, "dsi-tx,video-mode"))
dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM | SERDES_MIPI_DSI_MODE_VIDEO |
SERDES_MIPI_DSI_MODE_VIDEO_BURST;
else
dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM;
if (of_property_read_bool(np, "dsi-tx,eotp"))
dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_EOT_PACKET;
if (!of_property_read_string(np, "dsi-tx,format", &string)) {
if (!strcmp(string, "rgb666")) {
dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB666;
dsi_tx->bpp = 24;
} else if (!strcmp(string, "rgb666-packed")) {
dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB666_PACKED;
dsi_tx->bpp = 18;
} else if (!strcmp(string, "rgb565")) {
dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB565;
dsi_tx->bpp = 16;
} else {
dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB888;
dsi_tx->bpp = 24;
}
} else {
dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB888;
dsi_tx->bpp = 24;
}
ret = serdes_dsi_panel_get_cmds(sd_panel);
if (ret) {
dev_err(sd_panel->dev, "failed to get cmds\n");
return ret;
}
return 0;
}
static int serdes_panel_parse_dt(struct rk_serdes_panel *sd_panel)
{
struct rk_serdes_route *route = &sd_panel->route;
struct rk_serdes *serdes = sd_panel->parent;
u32 lanes;
int ret;
device_property_read_u32(sd_panel->dev, "local-port0", &route->local_port0);
device_property_read_u32(sd_panel->dev, "local-port1", &route->local_port1);
device_property_read_u32(sd_panel->dev, "remote0-port0", &route->remote0_port0);
device_property_read_u32(sd_panel->dev, "remote0-port1", &route->remote0_port1);
device_property_read_u32(sd_panel->dev, "remote1-port0", &route->remote1_port0);
device_property_read_u32(sd_panel->dev, "remote1-port1", &route->remote1_port1);
device_property_read_u32(sd_panel->dev, "num-lanes", &lanes);
serdes->route_flag = 0;
if (!route->local_port0) {
dev_err(sd_panel->dev, "local_port0 should set\n");
return -EINVAL;
}
if (!route->remote0_port0) {
dev_err(sd_panel->dev, "remote0_port0 should set\n");
return -EINVAL;
}
if (route->remote1_port0 && route->remote0_port1) {
dev_err(sd_panel->dev, "too many output\n");
return -EINVAL;
}
route->frame_mode = SERDES_FRAME_NORMAL_MODE;
/* 2 video stream output */
if (route->remote1_port0 || route->remote0_port1) {
if (route->remote1_port0)
serdes->route_flag |= ROUTE_MULTI_REMOTE | ROUTE_MULTI_CHANNEL |
ROUTE_MULTI_LANE;
if (route->remote0_port1) {
if ((route->remote0_port0 == RK_SERDES_LVDS_TX0) &&
(route->remote0_port1 == RK_SERDES_LVDS_TX1)) {
serdes->route_flag |= ROUTE_MULTI_CHANNEL;
} else if ((route->remote0_port0 == RK_SERDES_LVDS_TX1) &&
(route->remote0_port1 == RK_SERDES_LVDS_TX0)) {
serdes->route_flag |= ROUTE_MULTI_CHANNEL;
} else {
dev_err(sd_panel->dev, "invalid multi output type\n");
return -EINVAL;
}
if (lanes == 2)
serdes->route_flag |= ROUTE_MULTI_LANE;
}
if (route->local_port1) {
if ((route->local_port0 == RK_SERDES_DSI_RX0) &&
(route->local_port1 == RK_SERDES_DSI_RX1))
serdes->route_flag |= ROUTE_MULTI_DSI_INPUT;
else if ((route->local_port0 == RK_SERDES_DSI_RX1) &&
(route->local_port1 == RK_SERDES_DSI_RX0))
serdes->route_flag |= ROUTE_MULTI_DSI_INPUT;
else if ((route->local_port0 == RK_SERDES_LVDS_RX0) &&
(route->local_port1 == RK_SERDES_LVDS_RX1))
serdes->route_flag |= ROUTE_MULTI_LVDS_INPUT;
else if ((route->local_port0 == RK_SERDES_LVDS_RX1) &&
(route->local_port1 == RK_SERDES_LVDS_RX0))
serdes->route_flag |= ROUTE_MULTI_LVDS_INPUT;
else {
dev_err(sd_panel->dev, "invalid multi input type\n");
return -EINVAL;
}
serdes->route_flag |= ROUTE_MULTI_SOURCE;
} else {
if (device_property_read_bool(sd_panel->dev, "split-mode")) {
/* only dsi input support split mode */
if ((route->local_port0 != RK_SERDES_DSI_RX0) &&
(route->local_port0 != RK_SERDES_DSI_RX1)) {
dev_err(sd_panel->dev,
"local_port should be dsi in split mode\n");
return -EINVAL;
}
if (device_property_read_bool(sd_panel->dev,
"sf-pixel-interleaved"))
route->frame_mode = SERDES_SP_PIXEL_INTERLEAVED;
else if (device_property_read_bool(sd_panel->dev,
"sf-line-interleaved"))
route->frame_mode = SERDES_SP_LINE_INTERLEAVED;
else
route->frame_mode = SERDES_SP_LEFT_RIGHT_SPLIT;
serdes->route_flag |= ROUTE_MULTI_SPLIT;
} else {
serdes->route_flag |= ROUTE_MULTI_MIRROR;
}
}
} else {
if (lanes == 2)
serdes->route_flag |= ROUTE_MULTI_LANE;
}
if (route->remote0_port0 & RK_SERDES_DSI_TX0 ||
route->remote1_port0 & RK_SERDES_DSI_TX0) {
ret = rkx120_dsi_tx_parse(sd_panel);
if (ret) {
dev_err(sd_panel->dev, "failed to get cmds\n");
return ret;
}
}
if (route->local_port0 & RK_SERDES_DSI_RX0 ||
route->local_port0 & RK_SERDES_DSI_TX1) {
ret = rkx120_dsi_rx_parse(sd_panel);
if (ret < 0)
return ret;
}
return 0;
}
static int serdes_panel_probe(struct platform_device *pdev)
{
struct rk_serdes *serdes = dev_get_drvdata(pdev->dev.parent);
struct rk_serdes_panel *sd_panel;
int ret;
sd_panel = devm_kzalloc(&pdev->dev, sizeof(*sd_panel), GFP_KERNEL);
if (!sd_panel)
return -ENOMEM;
sd_panel->dev = &pdev->dev;
sd_panel->parent = serdes;
sd_panel->route.stream_type = STREAM_DISPLAY;
serdes->vm = &sd_panel->route.vm;
sd_panel->supply = devm_regulator_get_optional(sd_panel->dev, "power");
if (IS_ERR(sd_panel->supply)) {
ret = PTR_ERR(sd_panel->supply);
if (ret != -ENODEV) {
if (ret != -EPROBE_DEFER)
dev_err(sd_panel->dev, "failed to request regulator: %d\n",
ret);
return ret;
}
sd_panel->supply = NULL;
}
/* Get GPIOs and backlight controller. */
sd_panel->enable_gpio = devm_gpiod_get_optional(sd_panel->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(sd_panel->enable_gpio)) {
ret = PTR_ERR(sd_panel->enable_gpio);
dev_err(sd_panel->dev, "failed to request %s GPIO: %d\n",
"enable", ret);
return ret;
}
sd_panel->reset_gpio = devm_gpiod_get_optional(sd_panel->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(sd_panel->reset_gpio)) {
ret = PTR_ERR(sd_panel->reset_gpio);
dev_err(sd_panel->dev, "failed to request %s GPIO: %d\n",
"reset", ret);
return ret;
}
/* Register the panel. */
drm_panel_init(&sd_panel->panel, sd_panel->dev, &serdes_panel_funcs, 0);
ret = drm_panel_of_backlight(&sd_panel->panel);
if (ret)
return ret;
drm_panel_add(&sd_panel->panel);
dev_set_drvdata(sd_panel->dev, sd_panel);
ret = serdes_panel_parse_dt(sd_panel);
if (ret < 0) {
drm_panel_remove(&sd_panel->panel);
return ret;
}
return 0;
}
static int serdes_panel_remove(struct platform_device *pdev)
{
struct rk_serdes_panel *sd_panel = dev_get_drvdata(&pdev->dev);
drm_panel_remove(&sd_panel->panel);
drm_panel_disable(&sd_panel->panel);
return 0;
}
static const struct of_device_id serdes_panel_of_table[] = {
{ .compatible = "rockchip,serdes-panel", },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, serdes_panel_of_table);
static struct platform_driver serdes_panel_driver = {
.probe = serdes_panel_probe,
.remove = serdes_panel_remove,
.driver = {
.name = "serdes-panel",
.of_match_table = serdes_panel_of_table,
},
};
module_platform_driver(serdes_panel_driver);
MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
MODULE_DESCRIPTION("RKx110 RKx120 Panel Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Andy Yan <Andy.yan@rock-chips.com>
*/
#include <dt-bindings/mfd/rockchip-serdes.h>
#include <linux/debugfs.h>
#include "hal/cru_api.h"
#include "hal/pinctrl_api.h"
#include "rkx110_x120.h"
#include "rkx120_reg.h"
#include "serdes_combphy.h"
#if defined(CONFIG_DEBUG_FS)
static struct pattern_gen rkx120_pattern_gen[] = {
{
.name = "dsi",
.base = RKX120_PATTERN_GEN_DSI_BASE,
.link_src_reg = DES_GRF_SOC_CON2,
.link_src_offset = 12,
}, {
.name = "lvds0",
.base = RKX120_PATTERN_GEN_LVDS0_BASE,
.link_src_reg = DES_GRF_SOC_CON2,
.link_src_offset = 13,
}, {
.name = "lvds1",
.base = RKX120_PATTERN_GEN_LVDS1_BASE,
.link_src_reg = DES_GRF_SOC_CON2,
.link_src_offset = 14,
},
{ /* sentinel */ }
};
static const struct rk_serdes_reg rkx120_regs[] = {
{
.name = "grf",
.reg_base = RKX120_DES_GRF_BASE,
.reg_len = 0x410,
},
{
.name = "cru",
.reg_base = RKX120_DES_CRU_BASE,
.reg_len = 0xF04,
},
{
.name = "dvp",
.reg_base = RKX120_DVP_TX_BASE,
.reg_len = 0x1d0,
},
{
.name = "grf_mipi0",
.reg_base = RKX120_GRF_MIPI0_BASE,
.reg_len = 0x600,
},
{
.name = "grf_mipi1",
.reg_base = RKX120_GRF_MIPI1_BASE,
.reg_len = 0x600,
},
{
.name = "mipi_lvds_phy0",
.reg_base = RKX120_MIPI_LVDS_TX_PHY0_BASE,
.reg_len = 0x70,
},
{
.name = "mipi_lvds_phy1",
.reg_base = RKX120_MIPI_LVDS_TX_PHY1_BASE,
.reg_len = 0x70,
},
{
.name = "dsihost",
.reg_base = RKX120_DSI_TX_BASE,
.reg_len = 0x190,
},
{
.name = "gpio0",
.reg_base = RKX120_GPIO0_TX_BASE,
.reg_len = 0x80,
},
{
.name = "gpio1",
.reg_base = RKX120_GPIO1_TX_BASE,
.reg_len = 0x80,
},
{
.name = "csi_tx0",
.reg_base = RKX120_CSI_TX0_BASE,
.reg_len = 0x1D0,
},
{
.name = "csi_tx1",
.reg_base = RKX120_CSI_TX1_BASE,
.reg_len = 0x1D0,
},
{
.name = "rklink",
.reg_base = RKX120_DES_RKLINK_BASE,
.reg_len = 0xD4,
},
{
.name = "pcs0",
.reg_base = RKX120_DES_PCS0_BASE,
.reg_len = 0x1c0,
},
{
.name = "pcs1",
.reg_base = RKX120_DES_PCS1_BASE,
.reg_len = 0x1c0,
},
{
.name = "pma0",
.reg_base = RKX120_DES_PMA0_BASE,
.reg_len = 0x100,
},
{
.name = "pma1",
.reg_base = RKX120_DES_PMA1_BASE,
.reg_len = 0x100,
},
{
.name = "dsi_pattern_gen",
.reg_base = RKX120_PATTERN_GEN_DSI_BASE,
.reg_len = 0x18,
},
{
.name = "lvds0_pattern_gen",
.reg_base = RKX120_PATTERN_GEN_LVDS0_BASE,
.reg_len = 0x18,
},
{
.name = "lvds1_pattern_gen",
.reg_base = RKX120_PATTERN_GEN_LVDS1_BASE,
.reg_len = 0x18,
},
{ /* sentinel */ }
};
static int rkx120_reg_show(struct seq_file *s, void *v)
{
const struct rk_serdes_reg *regs = rkx120_regs;
struct rk_serdes_chip *chip = s->private;
struct rk_serdes *serdes = chip->serdes;
struct i2c_client *client = chip->client;
int i;
u32 val = 0;
seq_printf(s, "rkx120_%s:\n", file_dentry(s->file)->d_iname);
while (regs->name) {
if (!strcmp(regs->name, file_dentry(s->file)->d_iname))
break;
regs++;
}
if (!regs->name)
return -ENODEV;
for (i = 0; i <= regs->reg_len; i += 4) {
serdes->i2c_read_reg(client, regs->reg_base + i, &val);
if (i % 16 == 0)
seq_printf(s, "\n0x%04x:", i);
seq_printf(s, " %08x", val);
}
seq_puts(s, "\n");
return 0;
}
static ssize_t rkx120_reg_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
const struct rk_serdes_reg *regs = rkx120_regs;
struct rk_serdes_chip *chip = file->f_path.dentry->d_inode->i_private;
struct rk_serdes *serdes = chip->serdes;
struct i2c_client *client = chip->client;
u32 addr;
u32 val;
char kbuf[25];
int ret;
if (count >= sizeof(kbuf))
return -ENOSPC;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
ret = sscanf(kbuf, "%x%x", &addr, &val);
if (ret != 2)
return -EINVAL;
while (regs->name) {
if (!strcmp(regs->name, file_dentry(file)->d_iname))
break;
regs++;
}
if (!regs->name)
return -ENODEV;
addr += regs->reg_base;
serdes->i2c_write_reg(client, addr, val);
return count;
}
static int rkx120_reg_open(struct inode *inode, struct file *file)
{
struct rk_serdes_chip *chip = inode->i_private;
return single_open(file, rkx120_reg_show, chip);
}
static const struct file_operations rkx120_reg_fops = {
.owner = THIS_MODULE,
.open = rkx120_reg_open,
.read = seq_read,
.write = rkx120_reg_write,
.llseek = seq_lseek,
.release = single_release,
};
void rkx120_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry)
{
const struct rk_serdes_reg *regs = rkx120_regs;
struct pattern_gen *pattern_gen = rkx120_pattern_gen;
struct dentry *dir;
dir = debugfs_create_dir("registers", dentry);
if (!IS_ERR(dir)) {
while (regs->name) {
debugfs_create_file(regs->name, 0600, dir, chip, &rkx120_reg_fops);
regs++;
}
}
dir = debugfs_create_dir("pattern_gen", dentry);
if (!IS_ERR(dir)) {
while (pattern_gen->name) {
rkx110_x120_pattern_gen_debugfs_create_file(pattern_gen, chip, dir);
pattern_gen++;
}
}
}
#endif
static int rkx120_rgb_tx_iomux_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route,
u8 remote_id)
{
struct i2c_client *client = serdes->chip[remote_id].client;
uint32_t pins;
pins = RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 | RK_SERDES_GPIO_PIN_C2 |
RK_SERDES_GPIO_PIN_C3 | RK_SERDES_GPIO_PIN_C4 | RK_SERDES_GPIO_PIN_C5 |
RK_SERDES_GPIO_PIN_C6 | RK_SERDES_GPIO_PIN_C7;
serdes->set_hwpin(serdes, client, PIN_RKX120, RK_SERDES_DES_GPIO_BANK0, pins,
RK_SERDES_PIN_CONFIG_MUX_FUNC1);
pins = RK_SERDES_GPIO_PIN_A0 | RK_SERDES_GPIO_PIN_A1 | RK_SERDES_GPIO_PIN_A2 |
RK_SERDES_GPIO_PIN_A3 | RK_SERDES_GPIO_PIN_A4 | RK_SERDES_GPIO_PIN_A5 |
RK_SERDES_GPIO_PIN_A6 | RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0 |
RK_SERDES_GPIO_PIN_B1 | RK_SERDES_GPIO_PIN_B2 | RK_SERDES_GPIO_PIN_B3 |
RK_SERDES_GPIO_PIN_B4 | RK_SERDES_GPIO_PIN_B5 | RK_SERDES_GPIO_PIN_B6 |
RK_SERDES_GPIO_PIN_B7 | RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 |
RK_SERDES_GPIO_PIN_C2 | RK_SERDES_GPIO_PIN_C3;
serdes->set_hwpin(serdes, client, PIN_RKX120, RK_SERDES_DES_GPIO_BANK1, pins,
RK_SERDES_PIN_CONFIG_MUX_FUNC1);
return 0;
}
int rkx120_rgb_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route,
u8 remote_id)
{
rkx120_rgb_tx_iomux_cfg(serdes, route, remote_id);
return 0;
}
int rkx120_lvds_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
u8 phy_id)
{
struct i2c_client *client = serdes->chip[remote_id].client;
if (phy_id) {
serdes->i2c_write_reg(client, DES_GRF_SOC_CON7, 0xfff00630);
serdes->i2c_write_reg(client, DES_GRF_SOC_CON2, 0x00200020);
} else {
serdes->i2c_write_reg(client, DES_GRF_SOC_CON6, 0x0fff0063);
serdes->i2c_write_reg(client, DES_GRF_SOC_CON2, 0x00040004);
}
rkx120_combtxphy_set_mode(serdes, COMBTX_PHY_MODE_VIDEO_LVDS);
if (route->remote0_port0 & RK_SERDES_DUAL_LVDS_TX)
rkx120_combtxphy_set_rate(serdes, route->vm.pixelclock * 7 / 2);
else
rkx120_combtxphy_set_rate(serdes, route->vm.pixelclock * 7);
rkx120_combtxphy_power_on(serdes, remote_id, phy_id);
return 0;
}

View File

@@ -0,0 +1,416 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
*/
#include <linux/kernel.h>
#include <linux/iopoll.h>
#include "rkx110_x120.h"
#include "rkx120_reg.h"
#include "serdes_combphy.h"
#define CLANE_PARA0 0x0000
#define CLANE_PARA1 0x0004
#define T_INITTIME_C(x) UPDATE(x, 31, 0)
#define CLANE_PARA2 0x0008
#define T_CLKPREPARE_C(x) UPDATE(x, 23, 16)
#define T_CLKZERO_C(x) UPDATE(x, 15, 8)
#define T_CLKPRE_C(x) UPDATE(x, 7, 0)
#define CLANE_PARA3 0x000c
#define T_CLKPOST_C_MASK GENMASK(23, 16)
#define T_CLKPOST_C(x) UPDATE(x, 23, 16)
#define T_CLKTRAIL_C_MASK GENMASK(15, 8)
#define T_CLKTRAIL_C(x) UPDATE(x, 15, 8)
#define T_HSEXIT_C_MASK GENMASK(7, 0)
#define T_HSEXIT_C(x) UPDATE(x, 7, 0)
#define DLANE0_PARA0 0x0010
#define T_RST2ENLPTX_D(x) UPDATE(x, 15, 0)
#define DLANE0_PARA1 0x0014
#define T_INITTIME_D(x) UPDATE(x, 31, 0)
#define DLANE0_PARA2 0x0018
#define T_HSPREPARE_D(x) UPDATE(x, 31, 24)
#define T_HSZERO_D(x) UPDATE(x, 23, 16)
#define T_HSTRAIL_D(x) UPDATE(x, 15, 8)
#define T_HSEXIT_D(x) UPDATE(x, 7, 0)
#define DLANE0_PARA3 0x001c
#define T_WAKEUP_D(x) UPDATE(x, 31, 0)
#define DLANE0_PARA4 0x0020
#define T_TAGO_D0(x) UPDATE(x, 23, 16)
#define T_TASURE_D0(x) UPDATE(x, 15, 8)
#define T_TAGET_D0(x) UPDATE(x, 7, 0)
#define DLANE_PARA0(x) (0x0014 + (x * 0x0010))
#define DLANE_PARA1(x) (0x0018 + (x * 0x0010))
#define DLANE_PARA2(x) (0x001C + (x * 0x0010))
#define DLANE_PARA3(x) (0x0020 + (x * 0x0010))
#define COMMON_PARA0 (0x0054)
#define T_LPX(x) UPDATE(x, 7, 0)
#define CTRL_PARA0 0x0058
#define PWON_SEL BIT(3)
#define PWON_DSI BIT(1)
#define SU_IDDQ_EN BIT(0)
#define PLL_CTRL_PARA0 0x005c
#define PLL_LOCK BIT(27)
#define RATE_MASK GENMASK(26, 24)
#define RATE(x) UPDATE(x, 26, 24)
#define REFCLK_DIV_MASK GENMASK(23, 19)
#define REFCLK_DIV(x) UPDATE(x, 23, 19)
#define PLL_DIV_MASK GENMASK(18, 4)
#define PLL_DIV(x) UPDATE(x, 18, 4)
#define DSI_PIXELCLK_DIV(x) UPDATE(x, 3, 0)
#define PLL_CTRL_PARA1 0x0060
#define PLL_CTRL(x) UPDATE(x, 31, 0)
#define RCAL_CTRL 0x0064
#define RCAL_EN BIT(13)
#define RCAL_TRIM(x) UPDATE(x, 12, 9)
#define RCAL_DONE BIT(0)
#define TRIM_PARA 0x0068
#define HSTX_AMP_TRIM(x) UPDATE(x, 13, 11)
#define LPTX_SR_TRIM(x) UPDATE(x, 10, 8)
#define LPRX_VREF_TRIM(x) UPDATE(x, 7, 4)
#define LPCD_VREF_TRIM(x) UPDATE(x, 3, 0)
#define TEST_PARA0 0x006c
#define FSET_EN BIT(3)
#define CLANE_PARA4 0x0078
#define INTERFACE_PARA 0x007c
#define TXREADYESC_VLD(x) UPDATE(x, 15, 8)
#define RXVALIDESC_VLD(x) UPDATE(x, 7, 0)
#define GRF_MIPITX_CON0 0x0000
#define PHYSHUTDWN(x) HIWORD_UPDATE(x, GENMASK(10, 10), 10)
#define LVDS_REFCLK_DIV(x) HIWORD_UPDATE(x, GENMASK(9, 6), 6)
#define PHY_MODE(x) HIWORD_UPDATE(x, GENMASK(4, 3), 3)
#define RATE_LVDS(x) HIWORD_UPDATE(x, GENMASK(2, 1), 1)
#define GRF_MIPITX_CON1 0x0004
#define PWON_PLL(x) HIWORD_UPDATE(x, GENMASK(15, 15), 15)
#define LVDS_PLL_DIV(x) HIWORD_UPDATE(x, GENMASK(14, 0), 0)
#define GRF_MIPITX_CON5 0x0014
#define TXCLK_BUS_WIDTH(x) HIWORD_UPDATE(x, GENMASK(14, 12), 12)
#define TX3_BUS_WIDTH(x) HIWORD_UPDATE(x, GENMASK(11, 9), 9)
#define TX2_BUS_WIDTH(x) HIWORD_UPDATE(x, GENMASK(8, 6), 6)
#define TX1_BUS_WIDTH(x) HIWORD_UPDATE(x, GENMASK(5, 3), 3)
#define TX0_BUS_WIDTH(x) HIWORD_UPDATE(x, GENMASK(2, 0), 0)
#define GRF_MIPITX_CON6 0x0018
#define TX0_CTL_LOW(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define GRF_MIPITX_CON7 0x001c
#define TX1_CTL_LOW(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define GRF_MIPITX_CON8 0x0020
#define TX2_CTL_LOW(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define GRF_MIPITX_CON9 0x0024
#define TX3_CTL_LOW(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define GRF_MIPITX_CON10 0x0028
#define TXCK_CTL_LOW(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define GRF_MIPITX_CON13 0x0034
#define TX_IDLE(x) HIWORD_UPDATE(x, GENMASK(4, 0), 0)
#define GRF_MIPITX_CON14 0x0038
#define TX_PD(x) HIWORD_UPDATE(x, GENMASK(14, 10), 10)
#define GRF_MIPI_STATUS 0x0080
#define PHYLOCK BIT(0)
static void rkx120_combtxphy_dsi_timing_init(struct rk_serdes *des, u8 remote_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
const struct configure_opts_combphy cfg = combtxphy->mipi_dphy_cfg;
u32 byte_clk = DIV_ROUND_CLOSEST_ULL(combtxphy->rate, 8);
u32 esc_div = DIV_ROUND_UP(byte_clk, 20 * USEC_PER_SEC);
u64 t_byte_clk = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, byte_clk);
u32 t_clkprepare, t_clkzero, t_clkpre, t_clkpost, t_clktrail;
u32 t_init, t_hsexit, t_hsprepare, t_hszero, t_wakeup, t_hstrail;
u32 t_tago, t_tasure, t_taget;
u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
serdes_combphy_write(des, remote_id, base + INTERFACE_PARA,
TXREADYESC_VLD(esc_div - 2) |
RXVALIDESC_VLD(esc_div - 2));
serdes_combphy_write(des, remote_id, base + COMMON_PARA0, esc_div);
serdes_combphy_update_bits(des, remote_id, base + TEST_PARA0, FSET_EN, FSET_EN);
t_init = DIV_ROUND_UP(cfg.init, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + CLANE_PARA1, T_INITTIME_C(t_init));
serdes_combphy_write(des, remote_id, base + DLANE0_PARA1, T_INITTIME_D(t_init));
serdes_combphy_write(des, remote_id, base + DLANE_PARA1(1), T_INITTIME_D(t_init));
serdes_combphy_write(des, remote_id, base + DLANE_PARA1(2), T_INITTIME_D(t_init));
serdes_combphy_write(des, remote_id, base + DLANE_PARA1(3), T_INITTIME_D(t_init));
t_clkprepare = DIV_ROUND_UP(cfg.clk_prepare, t_byte_clk) - 1;
t_clkzero = DIV_ROUND_UP(cfg.clk_zero, t_byte_clk) - 1;
t_clkpre = DIV_ROUND_UP(cfg.clk_pre, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + CLANE_PARA2,
T_CLKPREPARE_C(t_clkprepare) |
T_CLKZERO_C(t_clkzero) | T_CLKPRE_C(t_clkpre));
t_clkpost = DIV_ROUND_UP(cfg.clk_post, t_byte_clk) - 1;
t_clktrail = DIV_ROUND_UP(cfg.clk_trail, t_byte_clk) - 1;
t_hsexit = DIV_ROUND_UP(cfg.hs_exit, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + CLANE_PARA3,
T_CLKPOST_C(t_clkpost) |
T_CLKTRAIL_C(t_clktrail) |
T_HSEXIT_C(t_hsexit));
t_hsprepare = DIV_ROUND_UP(cfg.hs_prepare, t_byte_clk) - 1;
t_hszero = DIV_ROUND_UP(cfg.hs_zero, t_byte_clk) - 1;
t_hstrail = DIV_ROUND_UP(cfg.hs_trail, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + DLANE0_PARA2,
T_HSPREPARE_D(t_hsprepare) |
T_HSZERO_D(t_hszero) |
T_HSTRAIL_D(t_hstrail) |
T_HSEXIT_D(t_hsexit));
serdes_combphy_write(des, remote_id, base + DLANE_PARA2(1),
T_HSPREPARE_D(t_hsprepare) |
T_HSZERO_D(t_hszero) |
T_HSTRAIL_D(t_hstrail) |
T_HSEXIT_D(t_hsexit));
serdes_combphy_write(des, remote_id, base + DLANE_PARA2(2),
T_HSPREPARE_D(t_hsprepare) |
T_HSZERO_D(t_hszero) |
T_HSTRAIL_D(t_hstrail) |
T_HSEXIT_D(t_hsexit));
serdes_combphy_write(des, remote_id, base + DLANE_PARA2(3),
T_HSPREPARE_D(t_hsprepare) |
T_HSZERO_D(t_hszero) |
T_HSTRAIL_D(t_hstrail) |
T_HSEXIT_D(t_hsexit));
t_wakeup = DIV_ROUND_UP(cfg.wakeup, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + DLANE0_PARA3, T_WAKEUP_D(t_wakeup));
serdes_combphy_write(des, remote_id, base + DLANE_PARA3(1), T_WAKEUP_D(t_wakeup));
serdes_combphy_write(des, remote_id, base + DLANE_PARA3(2), T_WAKEUP_D(t_wakeup));
serdes_combphy_write(des, remote_id, base + DLANE_PARA3(3), T_WAKEUP_D(t_wakeup));
serdes_combphy_write(des, remote_id, base + CLANE_PARA4, T_WAKEUP_D(t_wakeup));
t_tago = DIV_ROUND_UP(cfg.ta_go, t_byte_clk) - 1;
t_tasure = DIV_ROUND_UP(cfg.ta_sure, t_byte_clk) - 1;
t_taget = DIV_ROUND_UP(cfg.ta_get, t_byte_clk) - 1;
serdes_combphy_write(des, remote_id, base + DLANE0_PARA4,
T_TAGO_D0(t_tago) |
T_TASURE_D0(t_tasure) |
T_TAGET_D0(t_taget));
}
static void rkx120_combtxphy_dsi_pll_set(struct rk_serdes *des, u8 remote_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
serdes_combphy_update_bits(des, remote_id, base + PLL_CTRL_PARA0,
RATE_MASK | REFCLK_DIV_MASK | PLL_DIV_MASK,
RATE(combtxphy->rate_factor) |
REFCLK_DIV(combtxphy->ref_div - 1) |
PLL_DIV(combtxphy->fb_div));
}
static void rkx120_combtxphy_dsi_power_on(struct rk_serdes *des, u8 remote_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
struct i2c_client *client = des->chip[remote_id].client;
u32 grf_base = RKX120_GRF_MIPI0_BASE;
u32 val;
int ret;
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
PHY_MODE(COMBTX_PHY_MODE_VIDEO_MIPI));
serdes_combphy_get_default_config(combtxphy->rate, &combtxphy->mipi_dphy_cfg);
rkx120_combtxphy_dsi_timing_init(des, remote_id);
rkx120_combtxphy_dsi_pll_set(des, remote_id);
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
ret = read_poll_timeout(des->i2c_read_reg, ret,
!(ret < 0) && (val & PLL_LOCK),
0, MSEC_PER_SEC, false, client,
RKX120_MIPI_LVDS_TX_PHY0_BASE + PLL_CTRL_PARA0,
&val);
if (ret < 0)
dev_err(des->dev, "PLL is not locked\n");
}
static void rkx120_combtxphy_dsi_power_off(struct rk_serdes *des, u8 remote_id)
{
struct i2c_client *client = des->chip[remote_id].client;
u32 grf_base = RKX120_GRF_MIPI0_BASE;
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(0));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(0));
}
static void rkx120_combtxphy_lvds_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
struct i2c_client *client = des->chip[remote_id].client;
u32 grf_base = (phy_id == 0) ?
RKX120_GRF_MIPI0_BASE : RKX120_GRF_MIPI1_BASE;
const struct {
unsigned long max_lane_mbps;
u8 rate_lvds;
u8 refclk_div;
u16 pll_div;
} hsfreqrange_table[] = {
{250, 2, 0, 0x3800},
{500, 1, 1, 0x3800},
{1000, 0, 3, 0x3800},
};
int ret;
u32 val;
u8 index;
for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
if (combtxphy->rate < hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
break;
if (index == ARRAY_SIZE(hsfreqrange_table))
--index;
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0x1f));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0x1f));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
LVDS_REFCLK_DIV(hsfreqrange_table[index].refclk_div) |
RATE_LVDS(hsfreqrange_table[index].rate_lvds));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1,
LVDS_PLL_DIV(hsfreqrange_table[index].pll_div));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON5,
TXCLK_BUS_WIDTH(2) | TX3_BUS_WIDTH(2) |
TX2_BUS_WIDTH(2) | TX1_BUS_WIDTH(2) |
TX0_BUS_WIDTH(2));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON6, TX0_CTL_LOW(0x80));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON7, TX0_CTL_LOW(0x80));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON8, TX0_CTL_LOW(0x80));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON9, TX0_CTL_LOW(0x80));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON10, TX0_CTL_LOW(0x80));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1) |
PHY_MODE(COMBTX_PHY_MODE_VIDEO_LVDS));
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
ret = read_poll_timeout(des->i2c_read_reg, ret,
!(ret < 0) && (val & PHYLOCK),
0, MSEC_PER_SEC, false, client,
grf_base + GRF_MIPI_STATUS, &val);
if (ret < 0)
dev_err(des->dev, "PLL is not locked\n");
des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0));
}
void rkx120_combtxphy_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
switch (combtxphy->mode) {
case COMBTX_PHY_MODE_VIDEO_MIPI:
rkx120_combtxphy_dsi_power_on(des, remote_id);
break;
case COMBTX_PHY_MODE_VIDEO_LVDS:
rkx120_combtxphy_lvds_power_on(des, remote_id, phy_id);
break;
default:
break;
}
}
void rkx120_combtxphy_power_off(struct rk_serdes *des, u8 remote_id)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
switch (combtxphy->mode) {
case COMBTX_PHY_MODE_VIDEO_MIPI:
rkx120_combtxphy_dsi_power_off(des, remote_id);
break;
case COMBTX_PHY_MODE_VIDEO_LVDS:
break;
case COMBTX_PHY_MODE_GPIO:
break;
default:
break;
}
}
static void rkx120_combtxphy_dsi_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
{
const struct {
unsigned long max_lane_mbps;
u8 refclk_div;
u8 post_factor;
} hsfreqrange_table[] = {
{ 100, 1, 5 },
{ 200, 1, 4 },
{ 400, 1, 3 },
{ 800, 1, 2},
{ 1600, 1, 1},
};
u64 ref_clk = 24 * USEC_PER_SEC;
u64 fvco;
u16 fb_div;
u8 ref_div, post_div, index;
for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
if (rate <= hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
break;
if (index == ARRAY_SIZE(hsfreqrange_table))
--index;
/*
* FVCO = Fckref * 8 * PI_POST_DIV + PF_POST_DIV / R
* data_rate = FVCO / post_div;
*/
ref_div = hsfreqrange_table[index].refclk_div;
post_div = 1 << hsfreqrange_table[index].post_factor;
fvco = rate * post_div;
fb_div = DIV_ROUND_CLOSEST_ULL((fvco * ref_div) << 10, ref_clk * 8);
rate = DIV_ROUND_CLOSEST_ULL(ref_clk * 8 * fb_div,
(u64)(ref_div * post_div) << 10);
combtxphy->ref_div = ref_div;
combtxphy->fb_div = fb_div;
combtxphy->rate_factor = hsfreqrange_table[index].post_factor;
combtxphy->rate = rate;
}
static void rkx120_combtxphy_lvds_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
{
}
void rkx120_combtxphy_set_rate(struct rk_serdes *des, u64 rate)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
switch (combtxphy->mode) {
case COMBTX_PHY_MODE_VIDEO_MIPI:
rkx120_combtxphy_dsi_pll_calc_rate(combtxphy, rate);
break;
case COMBTX_PHY_MODE_VIDEO_LVDS:
rkx120_combtxphy_lvds_pll_calc_rate(combtxphy, rate);
break;
default:
break;
}
combtxphy->rate = rate;
}
u64 rkx120_combtxphy_get_rate(struct rk_serdes *des)
{
return des->combtxphy.rate;
}
void rkx120_combtxphy_set_mode(struct rk_serdes *des, enum combtx_phy_mode mode)
{
struct rkx120_combtxphy *combtxphy = &des->combtxphy;
combtxphy->mode = mode;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#ifndef RKX120_DSI_TX_H
#define RKX120_DSI_TX_H
#include "rkx110_x120.h"
int rkx120_dsi_tx_cmd_seq_xfer(struct rk_serdes *des, u8 remote_id,
struct panel_cmds *cmds);
void rkx120_dsi_tx_pre_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route, u8 remote_id);
void rkx120_dsi_tx_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route, u8 remote_id);
void rkx120_dsi_tx_post_disable(struct rk_serdes *serdes,
struct rk_serdes_route *route, u8 remote_id);
void rkx120_dsi_tx_disable(struct rk_serdes *serdes,
struct rk_serdes_route *route, u8 remote_id);
#endif

View File

@@ -0,0 +1,797 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Zhang Yubing <yubing.zhang@rock-chips.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <dt-bindings/mfd/rockchip-serdes.h>
#include "hal/cru_api.h"
#include "hal/pinctrl_api.h"
#include "rkx110_x120.h"
#include "rkx120_reg.h"
#define LINK_REG(x) ((x) + RKX120_DES_RKLINK_BASE)
#define RKLINK_DES_LANE_ENGINE_CFG LINK_REG(0x0000)
#define TRAIN_CLK_SEL_MASK GENMASK(31, 30)
#define TRAIN_CLK_SEL_E0 UPDATE(0, 31, 30)
#define TRAIN_CLK_SEL_E1 UPDATE(1, 31, 30)
#define TRAIN_CLK_SEL_I2S UPDATE(2, 31, 30)
#define DUAL_LVDS_CHANNEL_SWAP BIT(29)
#define VIDEO_FREQ_AUTO_EN BIT(28)
#define ENGINE1_2_LANE BIT(23)
#define ENGINE1_EN BIT(22)
#define ENGINE0_2_LANE BIT(21)
#define ENGINE0_EN BIT(20)
#define LANE1_DATA_WIDTH_8BIT UPDATE(0, 15, 14)
#define LANE1_DATA_WIDTH_16BIT UPDATE(1, 15, 14)
#define LANE1_DATA_WIDTH_24BIT UPDATE(2, 15, 14)
#define LANE1_DATA_WIDTH_32BIT UPDATE(3, 15, 14)
#define LANE0_DATA_WIDTH_8BIT UPDATE(0, 13, 12)
#define LANE0_DATA_WIDTH_16BIT UPDATE(1, 13, 12)
#define LANE0_DATA_WIDTH_24BIT UPDATE(2, 13, 12)
#define LANE0_DATA_WIDTH_32BIT UPDATE(3, 13, 12)
#define LANE0_EN BIT(4)
#define LANE1_EN BIT(5)
#define DES_EN BIT(0)
#define RKLINK_DES_LANE_ENGINE_DST LINK_REG(0x0004)
#define LANE0_ENGINE_CFG_MASK GENMASK(3, 0)
#define LANE0_ENGINE0 BIT(0)
#define LANE0_ENGINE1 BIT(1)
#define LANE1_ENGINE_CFG_MASK GENMASK(7, 4)
#define LANE1_ENGINE0 BIT(4)
#define LANE1_ENGINE1 BIT(5)
#define RKLINK_DES_DATA_ID_CFG LINK_REG(0x0008)
#define DATA_FIFO3_RD_ID_MASK GENMASK(30, 28)
#define DATA_FIFO3_RD_ID(x) UPDATE(x, 30, 28)
#define DATA_FIFO2_RD_ID_MASK GENMASK(26, 24)
#define DATA_FIFO2_RD_ID(x) UPDATE(x, 26, 24)
#define DATA_FIFO1_RD_ID_MASK GENMASK(22, 20)
#define DATA_FIFO1_RD_ID(x) UPDATE(x, 22, 20)
#define DATA_FIFO0_RD_ID_MASK GENMASK(18, 16)
#define DATA_FIFO0_RD_ID(x) UPDATE(x, 18, 16)
#define DATA_FIFO3_WR_ID_MASK GENMASK(14, 12)
#define DATA_FIFO3_WR_ID(x) UPDATE(x, 14, 12)
#define DATA_FIFO2_WR_ID_MASK GENMASK(10, 8)
#define DATA_FIFO2_WR_ID(x) UPDATE(x, 10, 8)
#define DATA_FIFO1_WR_ID_MASK GENMASK(6, 4)
#define DATA_FIFO1_WR_ID(x) UPDATE(x, 6, 4)
#define DATA_FIFO0_WR_ID_MASK GENMASK(2, 0)
#define DATA_FIFO0_WR_ID(x) UPDATE(x, 2, 0)
#define RKLINK_DES_ORDER_ID_CFG LINK_REG(0x000C)
#define ORDER_FIFO1_RD_ID_MASK GENMASK(22, 20)
#define ORDER_FIFO1_RD_ID(x) UPDATE(x, 22, 20)
#define ORDER_FIFO0_RD_ID_MASK GENMASK(18, 16)
#define ORDER_FIFO0_RD_ID(x) UPDATE(x, 18, 16)
#define ORDER_FIFO1_WR_ID_MASK GENMASK(6, 4)
#define ORDER_FIFO1_WR_ID(x) UPDATE(x, 6, 4)
#define ORDER_FIFO0_WR_ID_MASK GENMASK(2, 0)
#define ORDER_FIFO0_WR_ID(x) UPDATE(x, 2, 0)
#define RKLINK_DES_SOURCE_CFG LINK_REG(0x0024)
#define E1_CAMERA_SRC_CSI UPDATE(0, 23, 21)
#define E1_CAMERA_SRC_LVDS UPDATE(1, 23, 21)
#define E1_CAMERA_SRC_DVP UPDATE(2, 23, 21)
#define E1_DISPLAY_SRC_DSI UPDATE(0, 23, 21)
#define E1_DISPLAY_SRC_DUAL_LDVS UPDATE(1, 23, 21)
#define E1_DISPLAY_SRC_LVDS0 UPDATE(2, 23, 21)
#define E1_DISPLAY_SRC_LVDS1 UPDATE(3, 23, 21)
#define E1_DISPLAY_SRC_RGB UPDATE(5, 23, 21)
#define E1_STREAM_CAMERA UPDATE(0, 20, 20)
#define E1_STREAM_DISPLAY UPDATE(1, 20, 20)
#define E0_CAMERA_SRC_CSI UPDATE(0, 19, 17)
#define E0_CAMERA_SRC_LVDS UPDATE(1, 19, 17)
#define E0_CAMERA_SRC_DVP UPDATE(2, 19, 17)
#define E0_DISPLAY_SRC_DSI UPDATE(0, 19, 17)
#define E0_DISPLAY_SRC_DUAL_LDVS UPDATE(1, 19, 17)
#define E0_DISPLAY_SRC_LVDS0 UPDATE(2, 19, 17)
#define E0_DISPLAY_SRC_LVDS1 UPDATE(3, 19, 17)
#define E0_DISPLAY_SRC_RGB UPDATE(5, 19, 17)
#define E0_STREAM_CAMERA UPDATE(0, 16, 16)
#define E0_STREAM_DISPLAY UPDATE(1, 16, 16)
#define LANE1_ENGINE_ID(x) UPDATE(x, 7, 6)
#define LANE1_LANE_ID(x) UPDATE(x, 5, 5)
#define LNAE1_ID_SEL(x) UPDATE(x, 4, 4)
#define LANE0_ENGINE_ID(x) UPDATE(x, 3, 2)
#define LANE0_LANE_ID(x) UPDATE(x, 1, 1)
#define LNAE0_ID_SEL(x) UPDATE(x, 0, 0)
#define RKLINK_DES_REG01_ENGIN_DEL 0x0030
#define E1_ENGINE_DELAY(x) UPDATE(x, 31, 16)
#define E0_ENGINE_DELAY(x) UPDATE(x, 15, 0)
#define RKLINK_DES_REG_PATCH 0X0050
#define E3_FIRST_FRAME_DEL BIT(7)
#define E2_FIRST_FRAME_DEL BIT(6)
#define E1_FIRST_FRAME_DEL BIT(5)
#define E0_FIRST_FRAME_DEL BIT(4)
#define DES_RKLINK_STOP_CFG LINK_REG(0x009C)
#define STOP_AUDIO BIT(4)
#define STOP_E1 BIT(1)
#define STOP_E0 BIT(0)
#define RKLINK_DES_SPI_CFG LINK_REG(0x00C4)
#define RKLINK_DES_UART_CFG LINK_REG(0x00C8)
#define RKLINK_DES_GPIO_CFG LINK_REG(0x00CC)
#define GPIO_GROUP1_EN BIT(17)
#define GPIO_GROUP0_EN BIT(16)
#define PCS_REG(id, x) ((x) + RKX120_DES_PCS0_BASE + (id) * RKX120_DES_PMA_OFFSET)
#define PCS_REG00(id) PCS_REG(id, 0x00)
#define DES_PCS_DUAL_LANE_MODE_EN HIWORD_UPDATE(1, GENMASK(8, 8), 8)
#define DES_PCS_AUTO_START_EN HIWORD_UPDATE(1, GENMASK(4, 4), 4)
#define DES_PCS_ECU_MODE HIWORD_UPDATE(0, GENMASK(1, 1), 1)
#define DES_PCS_EN_MASK HIWORD_MASK(0, 0)
#define DES_PCS_EN HIWORD_UPDATE(1, GENMASK(0, 0), 0)
#define DES_PCS_DISABLE HIWORD_UPDATE(0, GENMASK(0, 0), 0)
#define PCS_REG04(id) PCS_REG(id, 0x04)
#define PCS_REG08(id) PCS_REG(id, 0x08)
#define PCS_REG10(id) PCS_REG(id, 0x10)
#define PCS_REG14(id) PCS_REG(id, 0x14)
#define PCS_REG18(id) PCS_REG(id, 0x18)
#define PCS_REG1C(id) PCS_REG(id, 0x1C)
#define PCS_REG20(id) PCS_REG(id, 0x20)
#define PCS_REG24(id) PCS_REG(id, 0x24)
#define PCS_REG28(id) PCS_REG(id, 0x28)
#define PCS_REG30(id) PCS_REG(id, 0x30)
#define PCS_REG34(id) PCS_REG(id, 0x34)
#define PCS_REG40(id) PCS_REG(id, 0x40)
#define PMA_REG(id, x) ((x) + RKX120_DES_PMA0_BASE + (id) * RKX120_DES_PMA_OFFSET)
#define DES_PMA_STATUS(id) PMA_REG(id, 0x00)
#define DES_PMA_FORCE_INIT_STA BIT(23)
#define DES_PMA_RX_LOST BIT(2)
#define DES_PMA_RX_PLL_LOCK BIT(1)
#define DES_PMA_RX_RDY BIT(0)
#define DES_PMA_CTRL(id) PMA_REG(id, 0x04)
#define DES_PMA_FORCE_INIT_MASK HIWORD_MASK(8, 8)
#define DES_PMA_FORCE_INIT_EN HIWORD_UPDATE(1, BIT(8), 8)
#define DES_PMA_FORCE_INIT_DISABLE HIWORD_UPDATE(0, BIT(8), 8)
#define DES_PMA_DUAL_CHANNEL HIWORD_UPDATE(1, BIT(3), 3)
#define DES_PMA_INIT_CNT_CLR_MASK HIWORD_MASK(2, 2)
#define DES_PMA_INIT_CNT_CLR HIWORD_UPDATE(1, BIT(2), 2)
#define DES_PMA_LOAD00(id) PMA_REG(id, 0x10)
#define PMA_RX_POL BIT(0)
#define PMA_RX_WIDTH BIT(1)
#define PMA_RX_MSBF_EN BIT(2)
#define PMA_PLL_PWRDN BIT(3)
#define DES_PMA_LOAD01(id) PMA_REG(id, 0x14)
#define DES_PMA_PLL_FORCE_LK(x) HIWORD_UPDATE(x, GENMASK(13, 13), 13)
#define DES_PMA_LOS_VTH(x) HIWORD_UPDATE(x, GENMASK(12, 11), 11)
#define DES_PMA_PD_CP_PD(x) HIWORD_UPDATE(x, GENMASK(10, 10), 10)
#define DES_PMA_PD_CP_FP(x) HIWORD_UPDATE(x, GENMASK(9, 9), 9)
#define DES_PMA_PD_LOOP_DIV(x) HIWORD_UPDATE(x, GENMASK(8, 8), 8)
#define DES_PMA_PD_PFD(x) HIWORD_UPDATE(x, GENMASK(7, 7), 7)
#define DES_PMA_PD_VBIAS(x) HIWORD_UPDATE(x, GENMASK(6, 6), 6)
#define DES_PMA_AFE_VOS_EN(x) HIWORD_UPDATE(x, GENMASK(5, 5), 5)
#define DES_PMA_PD_AFE(x) HIWORD_UPDATE(x, GENMASK(4, 4), 4)
#define DES_PMA_RX_RTERM(x) HIWORD_UPDATE(x, GENMASK(3, 0), 0)
#define DES_PMA_LOAD02(id) PMA_REG(id, 0x18)
#define DES_PMA_LOAD03(id) PMA_REG(id, 0x1C)
#define DES_PMA_LOAD04(id) PMA_REG(id, 0x20)
#define DES_PMA_LOAD05(id) PMA_REG(id, 0x24)
#define DES_PMA_PLL_REFCLK_DIV_MASK HIWORD_MASK(15, 12)
#define DES_PMA_PLL_REFCLK_DIV(x) HIWORD_UPDATE(x, GENMASK(15, 12), 12)
#define DES_PMA_LOAD06(id) PMA_REG(id, 0x28)
#define DES_PMA_MDATA_AMP_SEL(x) HIWORD_UPDATE(x, GENMASK(15, 14), 14)
#define DES_PMA_RX_TSEQ(x) HIWORD_UPDATE(x, GENMASK(13, 13), 13)
#define DES_PMA_FREZ_ADPT_EQ(x) HIWORD_UPDATE(x, GENMASK(12, 12), 12)
#define DES_PMA__ADPT_EQ_TRIM(x) HIWORD_UPDATE(x, GENMASK(11, 0), 0)
#define DES_PMA_LOAD07(id) PMA_REG(id, 0x2C)
#define DES_PMA_LOAD08(id) PMA_REG(id, 0x30)
#define DES_PMA_RX(x) HIWORD_UPDATE(x, GENMASK(15, 0), 0)
#define DES_PMA_LOAD09(id) PMA_REG(id, 0x34)
#define DES_PMA_PLL_DIV_MASK HIWORD_MASK(14, 0)
#define DES_PLL_I_POST_DIV(x) HIWORD_UPDATE(x, GENMASK(14, 10), 10)
#define DES_PLL_F_POST_DIV(x) HIWORD_UPDATE(x, GENMASK(9, 0), 0)
#define DES_PMA_PLL_DIV(x) HIWORD_UPDATE(x, GENMASK(14, 0), 0)
#define DES_PMA_LOAD0A(id) PMA_REG(id, 0x38)
#define DES_PMA_CLK_2X_DIV_MASK HIWORD_MASK(7, 0)
#define DES_PMA_CLK_2X_DIV(x) HIWORD_UPDATE(x, GENMASK(7, 0), 0)
#define DES_PMA_LOAD0B(id) PMA_REG(id, 0x3C)
#define DES_PMA_LOAD0C(id) PMA_REG(id, 0x40)
#define DES_PMA_FCK_VCO_MASK HIWORD_MASK(15, 15)
#define DES_PMA_FCK_VCO HIWORD_UPDATE(1, BIT(15), 15)
#define DES_PMA_FCK_VCO_DIV2 HIWORD_UPDATE(0, BIT(15), 15)
#define DES_PMA_LOAD0D(id) PMA_REG(id, 0x44)
#define DES_PMA_PLL_DIV4_MASK HIWORD_MASK(12, 12)
#define DES_PMA_PLL_DIV4 HIWORD_UPDATE(1, GENMASK(12, 12), 12)
#define DES_PMA_PLL_DIV8 HIWORD_UPDATE(0, GENMASK(12, 12), 12)
#define DES_PMA_LOAD0E(id) PMA_REG(id, 0x48)
#define DES_PMA_REG100(id) PMA_REG(id, 0x100)
static const struct rk_serdes_pt des_pt[] = {
{
/* gpi_gpo_0 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x10001,
.en_val = 0x10001,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x10,
.dir_val = 0x10,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_1 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x10002,
.en_val = 0x10002,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x20,
.dir_val = 0x20,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A6,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_2 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x10004,
.en_val = 0x10004,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x40,
.dir_val = 0x40,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A7,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_3 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x10008,
.en_val = 0x10008,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x80,
.dir_val = 0x80,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
},
},
}, {
/* gpi_gpo_4 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x20100,
.en_val = 0x20100,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x1000,
.dir_val = 0x1000,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B3,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* gpi_gpo_5 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x20200,
.en_val = 0x20200,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x2000,
.dir_val = 0x2000,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B4,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* gpi_gpo_6 */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x20400,
.en_val = 0x20400,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x4000,
.dir_val = 0x4000,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_B5,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough irq */
.en_reg = RKLINK_DES_GPIO_CFG,
.en_mask = 0x20800,
.en_val = 0x20800,
.dir_reg = RKLINK_DES_GPIO_CFG,
.dir_mask = 0x8000,
.dir_val = 0x8000,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A4,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
},
},
}, {
/* passthrough uart0 */
.en_reg = RKLINK_DES_UART_CFG,
.en_mask = 0x1,
.en_val = 0x1,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough uart1 */
.en_reg = RKLINK_DES_UART_CFG,
.en_mask = 0x2,
.en_val = 0x2,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
},
},
}, {
/* passthrough spi */
.en_reg = RKLINK_DES_SPI_CFG,
.en_mask = 0x4,
.en_val = 0x4,
.dir_reg = RKLINK_DES_SPI_CFG,
.dir_mask = 0x1,
.dir_val = 0,
.configs = 1,
{
{
.bank = RK_SERDES_DES_GPIO_BANK0,
.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6 |
RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
},
},
},
};
static int rk_des_get_stream_source(struct rk_serdes_route *route, u32 port, u8 engine_id)
{
if (route->stream_type == STREAM_DISPLAY) {
if (port & RK_SERDES_RGB_TX)
return engine_id ? E1_DISPLAY_SRC_RGB : E0_DISPLAY_SRC_RGB;
else if (port & RK_SERDES_LVDS_TX0)
return engine_id ? E1_DISPLAY_SRC_LVDS0 : E0_DISPLAY_SRC_LVDS0;
else if (port & RK_SERDES_LVDS_TX1)
return engine_id ? E1_DISPLAY_SRC_LVDS1 : E0_DISPLAY_SRC_LVDS1;
else if (port & RK_SERDES_DUAL_LVDS_TX)
return engine_id ? E1_DISPLAY_SRC_DUAL_LDVS : E0_DISPLAY_SRC_DUAL_LDVS;
else if (port & RK_SERDES_DSI_TX0)
return engine_id ? E1_DISPLAY_SRC_DSI : E0_DISPLAY_SRC_DSI;
else if (port & RK_SERDES_DSI_TX1)
return engine_id ? E1_DISPLAY_SRC_DSI : E0_DISPLAY_SRC_DSI;
} else {
return engine_id ? E1_CAMERA_SRC_CSI : E0_CAMERA_SRC_CSI;
}
return 0;
}
static void rk_serdes_link_rx_rgb_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route,
u8 remote_id)
{
struct i2c_client *client = serdes->chip[remote_id].client;
serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
E1_ENGINE_DELAY(2800) | E0_ENGINE_DELAY(2800));
serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL |
E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
}
static void rk_serdes_link_rx_lvds_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route,
u8 remote_id)
{
struct i2c_client *client = serdes->chip[remote_id].client;
serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
E1_ENGINE_DELAY(4096) | E0_ENGINE_DELAY(4096));
serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL |
E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
}
static void rk_serdes_link_rx_dsi_enable(struct rk_serdes *serdes,
struct rk_serdes_route *route,
u8 remote_id)
{
struct i2c_client *client = serdes->chip[remote_id].client;
serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
E1_ENGINE_DELAY(4096) | E0_ENGINE_DELAY(4096));
serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL|
E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
}
static int rk120_link_rx_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id)
{
struct hwclk *hwclk = serdes->chip[remote_id].hwclk;
struct i2c_client *client;
u32 stream_type;
u32 rx_src;
u32 ctrl_val, mask, val;
u32 lane0_dsource_id, lane1_dsource_id;
bool is_rx_dual_lanes;
bool is_rx_dual_channels;
if (route->stream_type == STREAM_DISPLAY) {
client = serdes->chip[remote_id].client;
stream_type = E0_STREAM_DISPLAY;
} else {
client = serdes->chip[DEVICE_LOCAL].client;
stream_type = E0_STREAM_CAMERA;
}
is_rx_dual_lanes = (serdes->route_flag & ROUTE_MULTI_LANE) &&
!(serdes->route_flag & ROUTE_MULTI_REMOTE);
is_rx_dual_channels = (serdes->route_flag & ROUTE_MULTI_CHANNEL) &&
!(serdes->route_flag & ROUTE_MULTI_REMOTE);
serdes->i2c_read_reg(client, RKLINK_DES_LANE_ENGINE_CFG, &ctrl_val);
ctrl_val &= ~LANE1_EN;
ctrl_val |= LANE0_EN;
ctrl_val |= ENGINE0_EN;
if (is_rx_dual_lanes) {
ctrl_val |= LANE1_EN;
if (is_rx_dual_channels)
ctrl_val |= ENGINE1_EN;
else
ctrl_val |= ENGINE0_2_LANE;
} else {
if (is_rx_dual_channels)
ctrl_val |= ENGINE1_EN;
}
serdes->i2c_write_reg(client, RKLINK_DES_LANE_ENGINE_CFG, ctrl_val);
mask = LANE0_ENGINE_CFG_MASK;
val = LANE0_ENGINE0;
if (is_rx_dual_lanes) {
if (is_rx_dual_channels) {
mask |= LANE1_ENGINE_CFG_MASK;
val |= LANE1_ENGINE1;
} else {
mask |= LANE1_ENGINE_CFG_MASK;
val |= LANE1_ENGINE0;
}
} else {
if (is_rx_dual_channels)
val |= LANE0_ENGINE1;
}
serdes->i2c_update_bits(client, RKLINK_DES_LANE_ENGINE_DST, mask, val);
serdes->i2c_read_reg(client, RKLINK_DES_SOURCE_CFG, &val);
val &= ~(LANE0_ENGINE_ID(1) | LANE0_LANE_ID(1) | LANE1_ENGINE_ID(1) |
LANE1_LANE_ID(1) | LNAE0_ID_SEL(1) | LNAE1_ID_SEL(1));
if (is_rx_dual_lanes) {
if (is_rx_dual_channels) {
val |= LANE0_ENGINE_ID(0);
val |= LANE0_LANE_ID(0);
val |= LNAE0_ID_SEL(1);
val |= LANE1_ENGINE_ID(1);
val |= LANE1_LANE_ID(0);
val |= LNAE1_ID_SEL(1);
stream_type |= E1_STREAM_DISPLAY;
} else {
val |= LANE0_ENGINE_ID(0);
val |= LANE0_LANE_ID(0);
val |= LNAE0_ID_SEL(1);
val |= LANE1_ENGINE_ID(0);
val |= LANE1_LANE_ID(1);
val |= LNAE0_ID_SEL(1);
}
} else {
if (is_rx_dual_channels) {
val |= LANE0_ENGINE_ID(0);
val |= LANE0_LANE_ID(0);
val |= LANE1_ENGINE_ID(1);
val |= LANE1_LANE_ID(0);
stream_type |= E1_STREAM_DISPLAY;
} else {
val |= LNAE0_ID_SEL(1);
}
}
val |= stream_type;
if (remote_id == DEVICE_REMOTE0)
rx_src = rk_des_get_stream_source(route, route->remote0_port0, 0);
else
rx_src = rk_des_get_stream_source(route, route->remote1_port0, 0);
val |= rx_src;
if (is_rx_dual_channels) {
rx_src = rk_des_get_stream_source(route, route->remote0_port1, 1);
val |= rx_src;
}
serdes->i2c_write_reg(client, RKLINK_DES_SOURCE_CFG, val);
if (is_rx_dual_lanes || is_rx_dual_channels) {
mask = DATA_FIFO0_WR_ID_MASK | DATA_FIFO1_WR_ID_MASK | DATA_FIFO2_WR_ID_MASK |
DATA_FIFO3_WR_ID_MASK;
mask |= DATA_FIFO0_RD_ID_MASK | DATA_FIFO1_RD_ID_MASK | DATA_FIFO2_RD_ID_MASK |
DATA_FIFO3_RD_ID_MASK;
if (is_rx_dual_channels) {
lane0_dsource_id = (0 << 1) | 0;
lane1_dsource_id = (1 << 1) | 0;
} else {
lane0_dsource_id = (0 << 1) | 0;
lane1_dsource_id = (0 << 1) | 1;
}
val = DATA_FIFO0_WR_ID(lane0_dsource_id) | DATA_FIFO1_WR_ID(lane0_dsource_id);
val |= DATA_FIFO0_RD_ID(lane0_dsource_id) | DATA_FIFO1_RD_ID(lane0_dsource_id);
val |= DATA_FIFO2_WR_ID(lane1_dsource_id) | DATA_FIFO3_WR_ID(lane1_dsource_id);
val |= DATA_FIFO2_RD_ID(lane1_dsource_id) | DATA_FIFO3_RD_ID(lane1_dsource_id);
serdes->i2c_update_bits(client, RKLINK_DES_DATA_ID_CFG, mask, val);
mask = ORDER_FIFO0_WR_ID_MASK | ORDER_FIFO1_WR_ID_MASK |
ORDER_FIFO0_RD_ID_MASK | ORDER_FIFO1_RD_ID_MASK;
val = ORDER_FIFO0_WR_ID(lane0_dsource_id) | ORDER_FIFO1_WR_ID(lane1_dsource_id) |
ORDER_FIFO0_RD_ID(lane0_dsource_id) | ORDER_FIFO1_RD_ID(lane1_dsource_id);
serdes->i2c_update_bits(client, RKLINK_DES_ORDER_ID_CFG, mask, val);
}
ctrl_val |= DES_EN;
serdes->i2c_write_reg(client, RKLINK_DES_LANE_ENGINE_CFG, ctrl_val);
hwclk_set_rate(hwclk, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, route->vm.pixelclock);
dev_info(serdes->dev, "RKX120_CPS_E0_CLK_RKLINK_RX_PRE:%d\n",
hwclk_get_rate(hwclk, RKX120_CPS_E0_CLK_RKLINK_RX_PRE));
if (is_rx_dual_channels) {
hwclk_set_rate(hwclk, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, route->vm.pixelclock);
dev_info(serdes->dev, "RKX120_CPS_E1_CLK_RKLINK_RX_PRE:%d\n",
hwclk_get_rate(hwclk, RKX120_CPS_E1_CLK_RKLINK_RX_PRE));
}
if (route->remote0_port0 == RK_SERDES_RGB_TX || route->remote1_port0 == RK_SERDES_RGB_TX)
rk_serdes_link_rx_rgb_enable(serdes, route, remote_id);
if (route->remote0_port0 == RK_SERDES_LVDS_TX0 ||
route->remote1_port0 == RK_SERDES_LVDS_TX0 ||
route->remote0_port0 == RK_SERDES_LVDS_TX1 ||
route->remote1_port0 == RK_SERDES_LVDS_TX1 ||
route->remote0_port0 == RK_SERDES_DUAL_LVDS_TX)
rk_serdes_link_rx_lvds_enable(serdes, route, remote_id);
if (route->remote0_port0 == RK_SERDES_DSI_TX0 || route->remote1_port0 == RK_SERDES_DSI_TX0)
rk_serdes_link_rx_dsi_enable(serdes, route, remote_id);
return 0;
}
static int rk120_des_pcs_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route,
u8 remote_id, u8 pcs_id)
{
return 0;
}
static int rk120_des_pma_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
u8 pcs_id)
{
return 0;
}
int rkx120_linkrx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id)
{
rk120_link_rx_cfg(serdes, route, remote_id);
rk120_des_pcs_cfg(serdes, route, remote_id, 0);
rk120_des_pma_cfg(serdes, route, remote_id, 0);
if ((serdes->route_flag & ROUTE_MULTI_LANE) &&
!(serdes->route_flag & ROUTE_MULTI_REMOTE)) {
rk120_des_pcs_cfg(serdes, route, remote_id, 1);
rk120_des_pma_cfg(serdes, route, remote_id, 1);
}
return 0;
}
void rkx120_linkrx_engine_enable(struct rk_serdes *serdes, u8 en_id, u8 dev_id, bool enable)
{
struct i2c_client *client = serdes->chip[dev_id].client;
if (en_id)
serdes->i2c_update_bits(client, DES_RKLINK_STOP_CFG, STOP_E1,
enable ? 0 : STOP_E1);
else
serdes->i2c_update_bits(client, DES_RKLINK_STOP_CFG, STOP_E0,
enable ? 0 : STOP_E0);
}
void rkx120_linkrx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
bool is_rx)
{
struct i2c_client *client = serdes->chip[client_id].client;
const struct rk_serdes_pt_pin *pt_pin = des_pt[func_id].pt_pins;
int i;
/* config link passthrough */
serdes->i2c_update_bits(client, des_pt[func_id].en_reg, des_pt[func_id].en_mask,
des_pt[func_id].en_val);
if (des_pt[func_id].en_reg)
serdes->i2c_update_bits(client, des_pt[func_id].dir_reg, des_pt[func_id].dir_mask,
is_rx ? des_pt[func_id].dir_val : ~des_pt[func_id].dir_val);
/* config passthrough pinctrl */
for (i = 0; i < des_pt[func_id].configs; i++) {
serdes->set_hwpin(serdes, client, PIN_RKX120, pt_pin[i].bank, pt_pin[i].pin,
is_rx ? pt_pin[i].incfgs : pt_pin[i].outcfgs);
}
}
void rkx120_linkrx_wait_link_ready(struct rk_serdes *serdes, u8 id)
{
struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
u32 val;
int ret;
int sta;
if (id)
sta = DES_PCS1_READY;
else
sta = DES_PCS0_READY;
ret = read_poll_timeout(serdes->i2c_read_reg, ret,
!(ret < 0) && (val & sta),
1000, USEC_PER_SEC, false, client,
DES_GRF_SOC_STATUS0, &val);
if (ret < 0)
dev_err(&client->dev, "wait link ready timeout: 0x%08x\n", val);
else
dev_info(&client->dev, "link success: 0x%08x\n", val);
}
static void rkx120_pma_link_config(struct rk_serdes *serdes, u8 pcs_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
serdes->i2c_write_reg(client, DES_PMA_LOAD08(pcs_id), DES_PMA_RX(0x23b1));
serdes->i2c_write_reg(client, DES_PMA_LOAD01(pcs_id), DES_PMA_LOS_VTH(0) |
DES_PMA_RX_RTERM(0x8));
serdes->i2c_write_reg(client, DES_PMA_LOAD06(pcs_id), DES_PMA_MDATA_AMP_SEL(0x3));
serdes->i2c_write_reg(client, DES_PMA_REG100(pcs_id), 0xffff0000);
}
void rkx120_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
u8 pcs_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
u32 val;
serdes->i2c_read_reg(client, DES_PMA_STATUS(pcs_id), &val);
if (val & DES_PMA_FORCE_INIT_STA)
serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_INIT_CNT_CLR_MASK,
DES_PMA_INIT_CNT_CLR);
if (pll->force_init_en)
serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_FORCE_INIT_MASK,
DES_PMA_FORCE_INIT_EN);
else
serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_FORCE_INIT_MASK,
DES_PMA_FORCE_INIT_DISABLE);
serdes->i2c_update_bits(client, DES_PMA_LOAD09(pcs_id), DES_PMA_PLL_DIV_MASK,
DES_PMA_PLL_DIV(pll->pll_div));
serdes->i2c_update_bits(client, DES_PMA_LOAD05(pcs_id), DES_PMA_PLL_REFCLK_DIV_MASK,
DES_PMA_PLL_REFCLK_DIV(pll->pll_refclk_div));
if (pll->pll_fck_vco_div2)
serdes->i2c_update_bits(client, DES_PMA_LOAD0C(pcs_id), DES_PMA_FCK_VCO_MASK,
DES_PMA_FCK_VCO_DIV2);
else
serdes->i2c_update_bits(client, DES_PMA_LOAD0C(pcs_id), DES_PMA_FCK_VCO_MASK,
DES_PMA_FCK_VCO);
if (pll->pll_div4)
serdes->i2c_update_bits(client, DES_PMA_LOAD0D(pcs_id), DES_PMA_PLL_DIV4_MASK,
DES_PMA_PLL_DIV4);
else
serdes->i2c_update_bits(client, DES_PMA_LOAD0D(pcs_id), DES_PMA_PLL_DIV4_MASK,
DES_PMA_PLL_DIV8);
serdes->i2c_update_bits(client, DES_PMA_LOAD0A(pcs_id), DES_PMA_CLK_2X_DIV_MASK,
DES_PMA_CLK_2X_DIV(pll->clk_div));
rkx120_pma_link_config(serdes, pcs_id, dev_id);
}
void rkx120_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
dev_info(serdes->dev, "%s: %d\n", __func__, enable);
if (enable)
serdes->i2c_update_bits(client, PCS_REG00(pcs_id), DES_PCS_EN_MASK, DES_PCS_EN);
else
serdes->i2c_update_bits(client, PCS_REG00(pcs_id), DES_PCS_EN_MASK,
DES_PCS_DISABLE);
}
void rkx120_des_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 dev_id)
{
struct i2c_client *client = serdes->chip[dev_id].client;
u32 mask, val;
if (pma_id) {
mask = PMA1_EN_MASK;
val = enable ? PMA1_EN : PMA1_DISABLE;
} else {
mask = PMA0_EN_MASK;
val = enable ? PMA0_EN : PMA0_DISABLE;
}
serdes->i2c_update_bits(client, DES_GRF_SOC_CON4, mask, val);
}

View File

@@ -0,0 +1,468 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Zhang Yubing <yubing.zhang@rock-chips.com>
*/
#ifndef __X120_REG_H
#define __X120_REG_H
#include <linux/bits.h>
#define HIWORD_MASK(h, l) (GENMASK(h, l) | GENMASK(h, l) << 16)
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
#define HIWORD_UPDATE(v, m, l) (((v) << (l)) | (m << 16))
/************** RKX120 DES RX ***************/
#define RKX120_DES_CRU_BASE 0x01000000
#define RKX120_DES_GRF_BASE 0x01010000
#define GRF_REG(x) ((x) + RKX120_DES_GRF_BASE)
#define DES_GRF_GPIO0A_IOMUX_L GRF_REG(0x0)
#define DES_GRF_GPIO0A_IOMUX_H GRF_REG(0x4)
#define DES_GRF_GPIO0B_IOMUX_L GRF_REG(0x8)
#define DES_GRF_GPIO0B_IOMUX_H GRF_REG(0xC)
#define DES_GRF_GPIO0C_IOMUX_L GRF_REG(0x10)
#define DES_GRF_GPIO0C_IOMUX_H GRF_REG(0x14)
#define DES_GRF_GPIO0A_PULL_EN GRF_REG(0x20)
#define DES_GRF_GPIO0B_PULL_EN GRF_REG(0x24)
#define DES_GRF_GPIO0C_PULL_EN GRF_REG(0x28)
#define DES_GRF_GPIO1A_IOMUX GRF_REG(0x80)
#define DES_GRF_GPIO1B_IOMUX GRF_REG(0x84)
#define DES_GRF_GPIO1C_IOMUX GRF_REG(0x88)
#define DES_GRF_GPIO1A_PULL_CFG GRF_REG(0x90)
#define DES_GRF_GPIO1B_PULL_CFG GRF_REG(0x94)
#define DES_GRF_GPIO1C_PULL_CFG GRF_REG(0x98)
#define DES_GRF_SOC_CON2 GRF_REG(0x108)
#define DES_GRF_SOC_STATUS0 GRF_REG(0x160)
enum {
/* GPIO0A_IOMUX_H */
GPIO0A7_SHIFT = 12,
GPIO0A7_MASK = GENMASK(14, 12),
GPIO0A7_GPIO = 0,
GPIO0A7_SPI_MISO_M,
GPIO0A7_SPI_MISO_S,
GPIO0A7_UART1_TX_M,
GPIO0A7_UART1_TX_S,
GPIO0A7_GPO_2,
GPIO0A7_GPI_2,
GPIO0A7_PWM_0,
GPIO0A6_SHIFT = 8,
GPIO0A6_MASK = GENMASK(10, 8),
GPIO0A6_GPIO = 0,
GPIO0A6_SPI_MOSI_M,
GPIO0A6_SPI_MOSI_S,
GPIO0A6_UART0_RX_M,
GPIO0A6_UART0_RX_S,
GPIO0A6_GPO_1,
GPIO0A6_GPI_1,
GPIO0A6_I2C_DEBUG_SDA,
GPIO0A5_SHIFT = 4,
GPIO0A5_MASK = GENMASK(6, 4),
GPIO0A5_GPIO = 0,
GPIO0A5_SPI_CLK_M,
GPIO0A5_SPI_CLK_S,
GPIO0A5_UART0_TX_M,
GPIO0A5_UART0_TX_S,
GPIO0A5_GPO_0,
GPIO0A5_GPI_0,
GPIO0A5_I2C_DEBUG_SCL,
GPIO0A4_SHIFT = 0,
GPIO0A4_MASK = GENMASK(2, 0),
GPIO0A4_GPIO = 0,
GPIO0A4_INT_RX,
GPIO0A4_INT_TX,
/* GPIO0B_IOMUX_L */
GPIO0B3_SHIFT = 12,
GPIO0B3_MASK = GENMASK(14, 12),
GPIO0B3_GPIO = 0,
GPIO0B3_I2S_SDO0,
GPIO0B3_GPI_4,
GPIO0B3_GPO_4,
GPIO0B3_TP2,
GPIO0B2_SHIFT = 8,
GPIO0B2_MASK = GENMASK(10, 8),
GPIO0B2_GPIO = 0,
GPIO0B2_I2S_LRCK_M,
GPIO0B2_I2S_LRCK_S,
GPIO0B2_TP1,
GPIO0B1_SHIFT = 4,
GPIO0B1_MASK = GENMASK(6, 4),
GPIO0B1_GPIO = 0,
GPIO0B1_I2S_SCLK_M,
GPIO0B1_I2S_SCLK_S,
GPIO0B1_TP0,
GPIO0B0_SHIFT = 0,
GPIO0B0_MASK = GENMASK(2, 0),
GPIO0B0_GPIO = 0,
GPIO0B0_SPI_CSN_M,
GPIO0B0_SPI_CSN_S,
GPIO0B0_UART1_RX_M,
GPIO0B0_UART1_RX_S,
GPIO0B0_GPO_3,
GPIO0B0_GPI_3,
GPIO0B0_PWM_1,
/* GPIO0B_IOMUX_H */
GPIO0B7_SHIFT = 12,
GPIO0B7_MASK = GENMASK(14, 12),
GPIO0B7_GPIO = 0,
GPIO0B7_I2S_MCLK,
GPIO0B7_TEST_CLK_OUT,
GPIO0B7_TP6,
GPIO0B6_SHIFT = 8,
GPIO0B6_MASK = GENMASK(10, 8),
GPIO0B6_GPIO = 0,
GPIO0B6_I2S_SDO3,
GPIO0B6_I2S_SDI0,
GPIO0B6_TP5,
GPIO0B5_SHIFT = 4,
GPIO0B5_MASK = GENMASK(6, 4),
GPIO0B5_GPIO = 0,
GPIO0B5_I2S_SDO2,
GPIO0B5_GPI_6,
GPIO0B5_GPO_6,
GPIO0B5_I2C1_SDA_M,
GPIO0B5_I2C1_SDA_S,
GPIO0B5_TP4,
GPIO0B4_SHIFT = 0,
GPIO0B4_MASK = GENMASK(2, 0),
GPIO0B4_GPIO = 0,
GPIO0B5_I2S_SDO1,
GPIO0B5_GPI_5,
GPIO0B5_GPO_5,
GPIO0B5_I2C1_SCL_M,
GPIO0B5_I2C1_SCL_S,
GPIO0B5_PWM2,
GPIO0B5_TP3,
/* GPIO0C_IOMUX_L */
GPIO0C4_SHIFT = 12,
GPIO0C4_MASK = GENMASK(14, 12),
GPIO0C4_GPIO = 0,
GPIO0C4_LCDC_D0,
GPIO0C4_CIF_D0,
GPIO0C4_TP11,
GPIO0C3_SHIFT = 9,
GPIO0C3_MASK = GENMASK(11, 9),
GPIO0C3_GPIO = 0,
GPIO0C3_LCDC_DEN,
GPIO0C3_TP10,
GPIO0C2_SHIFT = 6,
GPIO0C2_MASK = GENMASK(8, 6),
GPIO0C2_GPIO = 0,
GPIO0C2_LCDC_HSYNC,
GPIO0C2_CIF_HSYNC,
GPIO0C2_TP9,
GPIO0C1_SHIFT = 3,
GPIO0C1_MASK = GENMASK(5, 3),
GPIO0C1_GPIO = 0,
GPIO0C1_LCDC_VSYNC,
GPIO0C1_CIF_VSYNC,
GPIO0C1_TP8,
GPIO0C0_SHIFT = 0,
GPIO0C0_MASK = GENMASK(2, 0),
GPIO0C0_GPIO = 0,
GPIO0C0_LCDC_CLK,
GPIO0C0_CIF_CLK,
GPIO0C0_TP7,
/* GPIO0C_IOMUX_H */
GPIO0C7_SHIFT = 6,
GPIO0C7_MASK = GENMASK(8, 6),
GPIO0C7_GPIO = 0,
GPIO0C7_LCDC_D3,
GPIO0C7_CIF_D3,
GPIO0C7_TP14,
GPIO0C6_SHIFT = 3,
GPIO0C6_MASK = GENMASK(5, 3),
GPIO0C6_GPIO = 0,
GPIO0C6_LCDC_D2,
GPIO0C6_CIF_D2,
GPIO0C6_TP13,
GPIO0C5_SHIFT = 0,
GPIO0C5_MASK = GENMASK(2, 0),
GPIO0C5_GPIO = 0,
GPIO0C5_LCDC_D1,
GPIO0C5_CIF_D1,
GPIO0C5_TP12,
/* GPIO1A_IOMUX */
GPIO1A7_SHIFT = 14,
GPIO1A7_MASK = GENMASK(15, 14),
GPIO1A7_GPIO = 0,
GPIO1A7_LCDC_D11,
GPIO1A7_CIF_D11,
GPIO1A6_SHIFT = 12,
GPIO1A6_MASK = GENMASK(13, 12),
GPIO1A6_GPIO = 0,
GPIO1A6_LCDC_D10,
GPIO1A6_CIF_D10,
GPIO1A5_SHIFT = 10,
GPIO1A5_MASK = GENMASK(11, 10),
GPIO1A5_GPIO = 0,
GPIO1A5_LCDC_D9,
GPIO1A5_CIF_D9,
GPIO1A4_SHIFT = 8,
GPIO1A4_MASK = GENMASK(9, 8),
GPIO1A4_GPIO = 0,
GPIO1A4_LCDC_D8,
GPIO1A4_CIF_D8,
GPIO1A3_SHIFT = 6,
GPIO1A3_MASK = GENMASK(7, 6),
GPIO1A3_GPIO = 0,
GPIO1A3_LCDC_D7,
GPIO1A3_CIF_D7,
GPIO1A2_SHIFT = 4,
GPIO1A2_MASK = GENMASK(5, 4),
GPIO1A2_GPIO = 0,
GPIO1A2_LCDC_D6,
GPIO1A2_CIF_D6,
GPIO1A1_SHIFT = 2,
GPIO1A1_MASK = GENMASK(3, 2),
GPIO1A1_GPIO = 0,
GPIO1A1_LCDC_D5,
GPIO1A1_CIF_D5,
GPIO1A0_SHIFT = 0,
GPIO1A0_MASK = GENMASK(1, 0),
GPIO1A0_GPIO = 0,
GPIO1A0_LCDC_D4,
GPIO1A0_CIF_D4,
/* GPIO1B_IOMUX */
GPIO1B7_SHIFT = 14,
GPIO1B7_MASK = GENMASK(15, 14),
GPIO1B7_GPIO = 0,
GPIO1B7_LCDC_D19,
GPIO1B6_SHIFT = 12,
GPIO1B6_MASK = GENMASK(13, 12),
GPIO1B6_GPIO = 0,
GPIO1B6_LCDC_D18,
GPIO1B5_SHIFT = 10,
GPIO1B5_MASK = GENMASK(11, 10),
GPIO1B5_GPIO = 0,
GPIO1B5_LCDC_D17,
GPIO1B4_SHIFT = 8,
GPIO1B4_MASK = GENMASK(9, 8),
GPIO1B4_GPIO = 0,
GPIO1B4_LCDC_D16,
GPIO1B3_SHIFT = 6,
GPIO1B3_MASK = GENMASK(7, 6),
GPIO1B3_GPIO = 0,
GPIO1B3_LCDC_D15,
GPIO1B3_CIF_D15,
GPIO1B2_SHIFT = 4,
GPIO1B2_MASK = GENMASK(5, 4),
GPIO1B2_GPIO = 0,
GPIO1B2_LCDC_D14,
GPIO1B2_CIF_D14,
GPIO1B1_SHIFT = 2,
GPIO1B1_MASK = GENMASK(3, 2),
GPIO1B1_GPIO = 0,
GPIO1B1_LCDC_D13,
GPIO1B1_CIF_D13,
GPIO1B0_SHIFT = 0,
GPIO1B0_MASK = GENMASK(1, 0),
GPIO1B0_GPIO = 0,
GPIO1B0_LCDC_D12,
GPIO1B0_CIF_D12,
/* GPIO1C_IOMUX */
GPIO1C3_SHIFT = 6,
GPIO1C3_MASK = GENMASK(7, 6),
GPIO1C3_GPIO = 0,
GPIO1C3_LCDC_D23,
GPIO1C2_SHIFT = 4,
GPIO1C2_MASK = GENMASK(5, 4),
GPIO1C2_GPIO = 0,
GPIO1C2_LCDC_D22,
GPIO1C1_SHIFT = 2,
GPIO1C1_MASK = GENMASK(3, 2),
GPIO1C1_GPIO = 0,
GPIO1C1_LCDC_D21,
GPIO1C0_SHIFT = 0,
GPIO1C0_MASK = GENMASK(1, 0),
GPIO1C0_GPIO = 0,
GPIO1C0_LCDC_D20,
};
#define DES_GRF_SOC_CON0 GRF_REG(0x100)
#define DES_GRF_SOC_CON1 GRF_REG(0x104)
#define DES_GRF_SOC_CON2 GRF_REG(0x108)
#define DES_GRF_SOC_CON3 GRF_REG(0x10C)
#define DES_GRF_SOC_CON4 GRF_REG(0x110)
#define DES_GRF_SOC_CON5 GRF_REG(0x114)
#define DES_GRF_SOC_CON6 GRF_REG(0x118)
#define DES_GRF_SOC_CON7 GRF_REG(0x11C)
enum {
/* SOC_CON0 */
LVDS_ALIGN_EN_SHIFT = 14,
LVDS_ALIGN_EN_MASK = GENMASK(15, 14),
LVDS_ALIGN_DISABLE = 0,
LVDS_ALIGN_EN,
/* SOC_CON2 */
LVDS1_LINK_SEL_SHIFT = 14,
LVDS1_LINK_SEL_MASK = GENMASK(14, 14),
/* enable lvds source from pattern generation */
LINK_SEL_PG_DISABLE = 0,
LINK_SEL_PG_EN = 1,
LVDS0_LINK_SEL_SHIFT = 13,
LVDS0_LINK_SEL_MASK = GENMASK(13, 13),
DSI0_LINK_SEL_SHIFT = 12,
DSI0_LINK_SEL_MASK = GENMASK(12, 12),
LVDS1_MSB_SHIFT = 5,
LVDS1_MSB_MASK = GENMASK(5, 5),
LVDS_LSB = 0,
LVDS_MSB,
LVDS1_FORMAT_SHIFT = 4,
LVDS1_FORMAT_MASK = GENMASK(4, 3),
LVDS_FORMAT_VESA_24BIT = 0,
LVDS_FORMAT_JEIDA_24BIT,
LVDS_FORMAT_JEIDA_18BIT,
LVDS_FORMAT_VESA_18BIT,
LVDS0_MSB_SHIFT = 2,
LVDS0_MSB_MASK = GENMASK(2, 2),
LVDS0_FORMAT_SHIFT = 0,
LVDS0_FORMAT_MASK = GENMASK(1, 0),
/* SOC_CON3 */
/* SOC_CON4 */
PMA1_EN_SHIFT = 11,
PMA1_EN_MASK = HIWORD_MASK(11, 11),
PMA1_EN = HIWORD_UPDATE(1, BIT(11), 11),
PMA1_DISABLE = HIWORD_UPDATE(0, BIT(11), 11),
PMA0_EN_SHIFT = 10,
PMA0_EN_MASK = HIWORD_MASK(10, 10),
PMA0_EN = HIWORD_UPDATE(1, BIT(10), 10),
PMA0_DISABLE = HIWORD_UPDATE(0, BIT(10), 10),
RGB_DCLK_BYPASS_SHIFT = 9,
RGB_DCLK_BYPASS_MASK = GENMASK(9, 9),
RGB_DCLK_DCLK_DLY_SHIFT = 1,
RGB_DCLK_DCLK_DLY_MASK = GENMASK(8, 1),
RGB_DCLK_INV_SHIFT = 0,
RGB_DCLK_INV_MASK = GENMASK(0, 0),
/* SOC_CON5 */
MIPI_PHY0_MODE_SEL_SHIFT = 4,
MIPI_PHY0_MODE_SEL_MASK = GENMASK(7, 4),
MIPI_PHY0_MODE_DSI = 0,
MIPI_PHY0_MODE_CSI,
/* SOC_CON6 */
LVDS0_CLK_SOURCE_SHIFT = 0,
LVDS0_CLK_SOURCE_MASK = GENMASK(3, 0),
/* SOC_CON7 */
LVDS1_CLK_SOURCE_SHIFT = 4,
LVDS1_CLK_SOURCE_MASK = GENMASK(15, 4),
DSI0_DPI_UPDATE_CFG_SHIFT = 2,
DSI0_DPI_UPDATE_CFG_MASK = GENMASK(2, 2),
DSI0_DPI_REDUCED_COLOR_SHIFT = 1,
DSI0_DPI_REDUCED_COLOR_MASK = GENMASK(1, 1),
DSI0_DPI_DISABLE_SHIFT = 0,
DSI0_DPI_DISABLE_MASK = GENMASK(0, 0),
/* SOC_CON8 */
LDO_PLC_SEL_SHIFT = 8,
LDO_PLC_SEL_MASK = GENMASK(8, 8),
LDO_PLC_170 = 0,
LDO_PLC_270,
LDO_VOL_SEL_SHIFT = 4,
LDO_VOL_SEL_MASK = GENMASK(7, 4),
LDO_VOL_110 = 0,
LDO_PLC_115,
LDO_BG_TRIM_SHIFT = 4,
LDO_BG_TRIM_MASK = GENMASK(7, 4),
LDO_BG_TRIM_OUT = 0,
LDO_BG_TRIM_OUT_55_N = 0,
LDO_BG_TRIM_OUT_110_N,
/* SOC_CON9 */
/* DES_GRF_IRQ_EN */
/* DES_GRF_SOC_STATUS0 */
DES_PCS1_READY = BIT(1),
DES_PCS0_READY = BIT(0),
};
#define RKX120_DVP_TX_BASE 0x01020000
#define RKX120_DSI_TX_BASE 0x01030000
#define RKX120_CSI_TX0_BASE 0x01040000
#define RKX120_CSI_TX1_BASE 0x01050000
#define RKX120_GPIO0_TX_BASE 0x01060000
#define RKX120_GPIO1_TX_BASE 0x01068000
#define RKX120_DES_RKLINK_BASE 0x01070000
#define RKX120_DES_PCS0_BASE 0x01074000
#define RKX120_DES_PCS1_BASE 0x01075000
#define RKX120_DES_PCS_OFFSET 0x00001000
#define RKX120_PWM_BASE 0x01080000
#define RKX120_EFUSE_BASE 0x01090000
#define RKX120_MIPI_LVDS_TX_PHY0_BASE 0x010A0000
#define RKX120_MIPI_LVDS_TX_PHY1_BASE 0x010B0000
#define RKX120_GRF_MIPI0_BASE 0x010C0000
#define RKX120_GRF_MIPI1_BASE 0x010D0000
#define RKX120_DES_PMA0_BASE 0x010E0000
#define RKX120_DES_PMA1_BASE 0x010F0000
#define RKX120_DES_PMA_OFFSET 0x00010000
#define RKX120_PATTERN_GEN_DSI_BASE 0x01100000
#define RKX120_PATTERN_GEN_LVDS0_BASE 0x01110000
#define RKX120_PATTERN_GEN_LVDS1_BASE 0x01120000
#endif

View File

@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Rockchip Electronics Co. Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#include <linux/kernel.h>
#include "rkx110_x120.h"
#include "serdes_combphy.h"
int serdes_combphy_write(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 val)
{
struct i2c_client *client = serdes->chip[remote_id].client;
return serdes->i2c_write_reg(client, reg, val);
}
int serdes_combphy_read(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 *val)
{
struct i2c_client *client = serdes->chip[remote_id].client;
return serdes->i2c_read_reg(client, reg, val);
}
int serdes_combphy_update_bits(struct rk_serdes *serdes, u8 remote_id,
u32 reg, u32 mask, u32 val)
{
struct i2c_client *client = serdes->chip[remote_id].client;
return serdes->i2c_update_bits(client, reg, mask, val);
}
void serdes_combphy_get_default_config(u64 hs_clk_rate,
struct configure_opts_combphy *cfg)
{
unsigned long long ui;
ui = ALIGN(NSEC_PER_SEC, hs_clk_rate);
do_div(ui, hs_clk_rate);
cfg->clk_miss = 0;
cfg->clk_post = 60 + 52 * ui;
cfg->clk_pre = 8;
cfg->clk_prepare = 38;
cfg->clk_settle = 95;
cfg->clk_term_en = 0;
cfg->clk_trail = 60;
cfg->clk_zero = 262;
cfg->d_term_en = 0;
cfg->eot = 0;
cfg->hs_exit = 100;
cfg->hs_prepare = 40 + 4 * ui;
cfg->hs_zero = 105 + 6 * ui;
cfg->hs_settle = 85 + 6 * ui;
cfg->hs_skip = 40;
/*
* The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
* contains this formula as:
*
* T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
*
* where n = 1 for forward-direction HS mode and n = 4 for reverse-
* direction HS mode. There's only one setting and this function does
* not parameterize on anything other that ui, so this code will
* assumes that reverse-direction HS mode is supported and uses n = 4.
*/
cfg->hs_trail = max(4 * 8 * ui, 60 + 4 * 4 * ui);
/*
* Note that TINIT is considered a protocol-dependent parameter, and
* thus the exact requirements for TINIT,MASTER and TINIT,SLAVE (transmitter
* and receiver initialization Stop state lengths, respectively,) are defined
* by the protocol layer specification and are outside the scope of this document.
* However, the D-PHY specification does place a minimum bound on the lengths of
* TINIT,MASTER and TINIT,SLAVE, which each shall be no less than 100 µs. A protocol
* layer specification using the D-PHY specification may specify any values greater
* than this limit, for example, TINIT,MASTER 1 ms and TINIT,SLAVE = 500 to 800 µs
*/
cfg->init = NSEC_PER_SEC / MSEC_PER_SEC;
cfg->lpx = 50;
cfg->ta_get = 5 * cfg->lpx;
cfg->ta_go = 4 * cfg->lpx;
cfg->ta_sure = cfg->lpx;
cfg->wakeup = NSEC_PER_SEC / MSEC_PER_SEC;
}

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*
*/
#ifndef SERDES_COMBPHY_H
#define SERDES_COMBPHY_H
int serdes_combphy_write(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 val);
int serdes_combphy_read(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 *val);
int serdes_combphy_update_bits(struct rk_serdes *serdes, u8 remote_id,
u32 reg, u32 mask, u32 val);
void serdes_combphy_get_default_config(u64 hs_clk_rate,
struct configure_opts_combphy *cfg);
void rkx110_combrxphy_set_mode(struct rk_serdes *ser, enum combrx_phy_mode mode);
void rkx110_combrxphy_set_rate(struct rk_serdes *ser, u64 rate);
void rkx110_combrxphy_power_on(struct rk_serdes *ser, enum comb_phy_id id);
void rkx110_combrxphy_power_off(struct rk_serdes *ser, enum comb_phy_id id);
void rkx120_combtxphy_set_mode(struct rk_serdes *des, enum combtx_phy_mode mode);
void rkx120_combtxphy_set_rate(struct rk_serdes *des, u64 rate);
u64 rkx120_combtxphy_get_rate(struct rk_serdes *des);
void rkx120_combtxphy_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id);
void rkx120_combtxphy_power_off(struct rk_serdes *des, u8 remote_id);
#endif