// 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/delay.h>
|
#include <linux/debugfs.h>
|
#include <linux/init.h>
|
#include <linux/interrupt.h>
|
#include <linux/i2c.h>
|
#include <linux/gpio/consumer.h>
|
#include <linux/mfd/core.h>
|
#include "rkx110_x120.h"
|
#include "rkx110_reg.h"
|
#include "rkx110_dsi_rx.h"
|
#include "rkx120_dsi_tx.h"
|
#include "hal/cru_api.h"
|
#include "hal/pinctrl_api.h"
|
|
static const struct mfd_cell rkx110_x120_devs[] = {
|
/* 2 panel device for rkx110_x120 drm panel */
|
{
|
.name = "serdes-panel",
|
.of_compatible = "rockchip,serdes-panel",
|
},
|
};
|
|
static int rk_serdes_i2c_read(struct i2c_client *client, u32 addr, u32 *value)
|
{
|
struct i2c_msg xfer[2];
|
u32 reg;
|
u32 data;
|
int ret;
|
|
reg = cpu_to_le32(addr);
|
/* Write register */
|
xfer[0].addr = client->addr;
|
xfer[0].flags = 0;
|
xfer[0].len = 4;
|
xfer[0].buf = (u8 *)®
|
|
/* Read data */
|
xfer[1].addr = client->addr;
|
xfer[1].flags = I2C_M_RD;
|
xfer[1].len = 4;
|
xfer[1].buf = (u8 *)&data;
|
|
ret = i2c_transfer(client->adapter, xfer, 2);
|
if (ret == 2)
|
ret = 0;
|
else if (ret >= 0)
|
ret = -EIO;
|
|
*value = le32_to_cpu(data);
|
dev_dbg(&client->dev, "read: 0x%08x: 0x%08x\n", addr, *value);
|
|
return ret;
|
}
|
|
static int rk_serdes_i2c_write(struct i2c_client *client, u32 addr, u32 value)
|
{
|
struct i2c_msg xfer;
|
u32 reg;
|
u32 data;
|
u8 buf[8];
|
int ret;
|
|
reg = cpu_to_le32(addr);
|
data = cpu_to_le32(value);
|
memcpy(&buf[0], ®, 4);
|
memcpy(&buf[4], &data, 4);
|
|
/* Write address & data */
|
xfer.addr = client->addr;
|
xfer.flags = 0;
|
xfer.len = 8;
|
xfer.buf = buf;
|
|
dev_dbg(&client->dev, "write: 0x%08x: 0x%08x\n", addr, value);
|
ret = i2c_transfer(client->adapter, &xfer, 1);
|
if (ret == 1)
|
return 0;
|
else if (ret < 0)
|
return ret;
|
else
|
return -EIO;
|
}
|
|
static int rk_serdes_i2c_update_bits(struct i2c_client *client, u32 reg, u32 mask, u32 val)
|
{
|
u32 value;
|
int ret;
|
|
ret = rk_serdes_i2c_read(client, reg, &value);
|
if (ret)
|
return ret;
|
|
value &= ~mask;
|
value |= (val & mask);
|
ret = rk_serdes_i2c_write(client, reg, value);
|
if (ret)
|
return ret;
|
|
return 0;
|
}
|
|
static bool rk_serdes_debug_mode(struct rk_serdes *serdes)
|
{
|
return serdes->rkx110_debug || serdes->rkx120_debug;
|
}
|
|
static void rk_serdes_wait_link_ready(struct rk_serdes *serdes)
|
{
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
rkx110_linktx_wait_link_ready(serdes, 0);
|
if (serdes->route_flag & ROUTE_MULTI_LANE) {
|
rkx110_ser_pma_enable(serdes, true, 1, DEVICE_LOCAL);
|
if (!(serdes->route_flag & ROUTE_MULTI_REMOTE))
|
rkx120_des_pma_enable(serdes, true, 1, DEVICE_REMOTE0);
|
rkx110_linktx_wait_link_ready(serdes, 1);
|
}
|
|
} else {
|
rkx120_linkrx_wait_link_ready(serdes, 0);
|
if (serdes->route_flag & ROUTE_MULTI_LANE) {
|
rkx120_des_pma_enable(serdes, true, 1, DEVICE_LOCAL);
|
if (!(serdes->route_flag & ROUTE_MULTI_REMOTE))
|
rkx110_ser_pma_enable(serdes, true, 1, DEVICE_REMOTE0);
|
rkx120_linkrx_wait_link_ready(serdes, 1);
|
}
|
}
|
}
|
|
static void rk_serdes_print_rate(struct rk_serdes *serdes, enum rk_serdes_rate rate)
|
{
|
switch (rate) {
|
case RATE_4GBPS_83M:
|
dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 83Mbps\n");
|
break;
|
case RATE_4GBPS_125M:
|
dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 125Mbps\n");
|
break;
|
case RATE_4GBPS_250M:
|
dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 250Mbps\n");
|
break;
|
case RATE_4_5GBPS_140M:
|
dev_info(serdes->dev, "serdes set rate: 4.5Gbps, backward: 140Mbps\n");
|
break;
|
case RATE_4_8GBPS_150M:
|
dev_info(serdes->dev, "serdes set rate: 4.8Gbps, backward: 150Mbps\n");
|
break;
|
#if 0
|
case RATE_5GBPS_156M:
|
dev_info(serdes->dev, "serdes set rate: 5Gbps, backward: 156Mbps\n");
|
break;
|
case RATE_6GBPS_187M:
|
dev_info(serdes->dev, "serdes set rate: 6Gbps, backward: 187Mbps\n");
|
break;
|
#endif
|
case RATE_2GBPS_83M:
|
dev_info(serdes->dev, "serdes set rate: 2Gbps, backward: 83Mbps\n");
|
break;
|
default:
|
dev_info(serdes->dev, "serdes set rate: Unknown rate\n");
|
break;
|
}
|
}
|
|
static void rk_serdes_set_rate(struct rk_serdes *serdes, enum rk_serdes_rate rate)
|
{
|
struct rk_serdes_pma_pll rkx110_pll, rkx120_pll;
|
|
if (serdes->rate == rate)
|
return;
|
|
memset(&rkx110_pll, 0, sizeof(rkx110_pll));
|
memset(&rkx120_pll, 0, sizeof(rkx120_pll));
|
|
rk_serdes_print_rate(serdes, rate);
|
|
switch (rate) {
|
case RATE_4GBPS_83M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 21330;
|
rkx110_pll.clk_div = 5;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = false;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 21330;
|
rkx120_pll.clk_div = 23;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
case RATE_4GBPS_125M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 21330;
|
rkx110_pll.clk_div = 1;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 21330;
|
rkx120_pll.clk_div = 15;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
case RATE_4GBPS_250M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 21330;
|
rkx110_pll.clk_div = 0;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 21330;
|
rkx120_pll.clk_div = 7;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
case RATE_4_5GBPS_140M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 24000;
|
rkx110_pll.clk_div = 1;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 12000;
|
rkx120_pll.clk_div = 7;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
case RATE_4_8GBPS_150M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 26000;
|
rkx110_pll.clk_div = 1;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 13000;
|
rkx120_pll.clk_div = 7;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
#if 0
|
case RATE_5GBPS_156M:
|
rkx110_pll.rate_mode = FDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 26667;
|
rkx110_pll.clk_div = 3;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = 1;
|
rkx120_pll.pll_div = 26667;
|
rkx120_pll.clk_div = 31;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
case RATE_6GBPS_187M:
|
rkx110_pll.rate_mode = QDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 29000;
|
rkx110_pll.clk_div = 1;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 29000;
|
rkx120_pll.clk_div = 15;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
#endif
|
case RATE_2GBPS_83M:
|
rkx110_pll.rate_mode = HDR_RATE_MODE;
|
rkx110_pll.pll_div4 = true;
|
rkx110_pll.pll_div = 21330;
|
rkx110_pll.clk_div = 2;
|
rkx110_pll.pll_refclk_div = 0;
|
rkx110_pll.pll_fck_vco_div2 = true;
|
|
rkx120_pll.pll_div4 = true;
|
rkx120_pll.pll_div = 21330;
|
rkx120_pll.clk_div = 23;
|
rkx120_pll.pll_refclk_div = 0;
|
rkx120_pll.pll_fck_vco_div2 = true;
|
break;
|
default:
|
return;
|
}
|
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_LOCAL);
|
rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_REMOTE0);
|
if (serdes->route_flag & ROUTE_MULTI_LANE) {
|
rkx110_pma_set_rate(serdes, &rkx110_pll, 1, DEVICE_LOCAL);
|
if (serdes->route_flag & ROUTE_MULTI_REMOTE)
|
rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_REMOTE1);
|
else
|
rkx120_pma_set_rate(serdes, &rkx120_pll, 1, DEVICE_REMOTE0);
|
}
|
rkx110_pcs_enable(serdes, 0, 0, DEVICE_LOCAL);
|
usleep_range(1000, 2000);
|
rkx110_pcs_enable(serdes, 1, 0, DEVICE_LOCAL);
|
} else {
|
rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_LOCAL);
|
rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_REMOTE0);
|
if (serdes->route_flag & ROUTE_MULTI_LANE) {
|
rkx120_pma_set_rate(serdes, &rkx120_pll, 1, DEVICE_LOCAL);
|
if (serdes->route_flag & ROUTE_MULTI_REMOTE)
|
rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_REMOTE1);
|
else
|
rkx110_pma_set_rate(serdes, &rkx110_pll, 1, DEVICE_REMOTE0);
|
}
|
rkx120_pcs_enable(serdes, 0, 0, DEVICE_LOCAL);
|
usleep_range(1000, 2000);
|
rkx120_pcs_enable(serdes, 1, 0, DEVICE_LOCAL);
|
}
|
|
rk_serdes_wait_link_ready(serdes);
|
|
serdes->rate = rate;
|
}
|
|
static int rk_serdes_route_prepare(struct rk_serdes *serdes, struct rk_serdes_route *route)
|
{
|
if (rk_serdes_debug_mode(serdes))
|
return 0;
|
|
if (route->stream_type == STREAM_DISPLAY) {
|
switch (route->local_port0) {
|
case RK_SERDES_RGB_RX:
|
rkx110_rgb_rx_enable(serdes, route);
|
break;
|
case RK_SERDES_LVDS_RX0:
|
rkx110_lvds_rx_enable(serdes, route, 0);
|
if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
|
rkx110_lvds_rx_enable(serdes, route, 1);
|
break;
|
case RK_SERDES_LVDS_RX1:
|
rkx110_lvds_rx_enable(serdes, route, 1);
|
if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
|
rkx110_lvds_rx_enable(serdes, route, 0);
|
break;
|
case RK_SERDES_DUAL_LVDS_RX:
|
rkx110_lvds_rx_enable(serdes, route, 0);
|
rkx110_lvds_rx_enable(serdes, route, 1);
|
break;
|
case RK_SERDES_DSI_RX0:
|
rkx110_dsi_rx_enable(serdes, route, 0);
|
if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
|
rkx110_dsi_rx_enable(serdes, route, 1);
|
break;
|
case RK_SERDES_DSI_RX1:
|
rkx110_dsi_rx_enable(serdes, route, 1);
|
if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
|
rkx110_dsi_rx_enable(serdes, route, 0);
|
break;
|
default:
|
dev_info(serdes->dev, "undefined local port0");
|
return -EINVAL;
|
}
|
|
rkx110_linktx_enable(serdes, route);
|
|
rkx120_linkrx_enable(serdes, route, DEVICE_REMOTE0);
|
if (serdes->route_flag & ROUTE_MULTI_REMOTE)
|
rkx120_linkrx_enable(serdes, route, DEVICE_REMOTE1);
|
|
if (route->remote0_port0 & RK_SERDES_DSI_TX0)
|
rkx120_dsi_tx_pre_enable(serdes, route, DEVICE_REMOTE0);
|
if (route->remote1_port0 & RK_SERDES_DSI_TX0)
|
rkx120_dsi_tx_pre_enable(serdes, route, DEVICE_REMOTE1);
|
} else {
|
/* for camera stream */
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_route_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
|
{
|
if (rk_serdes_debug_mode(serdes))
|
return 0;
|
|
if (route->stream_type == STREAM_DISPLAY) {
|
switch (route->remote0_port0) {
|
case RK_SERDES_RGB_TX:
|
rkx120_rgb_tx_enable(serdes, route, DEVICE_REMOTE0);
|
break;
|
case RK_SERDES_LVDS_TX0:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
|
break;
|
case RK_SERDES_LVDS_TX1:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
|
break;
|
case RK_SERDES_DUAL_LVDS_TX:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
|
break;
|
case RK_SERDES_DSI_TX0:
|
rkx120_dsi_tx_enable(serdes, route, DEVICE_REMOTE0);
|
break;
|
default:
|
dev_err(serdes->dev, "undefined remote0_port0\n");
|
return -EINVAL;
|
}
|
|
if (serdes->route_flag & ROUTE_MULTI_REMOTE) {
|
switch (route->remote1_port0) {
|
case RK_SERDES_RGB_TX:
|
rkx120_rgb_tx_enable(serdes, route, DEVICE_REMOTE1);
|
break;
|
case RK_SERDES_LVDS_TX0:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 0);
|
break;
|
case RK_SERDES_LVDS_TX1:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 1);
|
break;
|
case RK_SERDES_DUAL_LVDS_TX:
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 0);
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 1);
|
break;
|
case RK_SERDES_DSI_TX0:
|
rkx120_dsi_tx_enable(serdes, route, DEVICE_REMOTE1);
|
break;
|
default:
|
dev_err(serdes->dev, "undefined remote1_port0\n");
|
return -EINVAL;
|
}
|
} else if (serdes->route_flag & ROUTE_MULTI_CHANNEL) {
|
if (route->remote0_port1 & RK_SERDES_LVDS_TX0) {
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
|
} else if (route->remote0_port1 & RK_SERDES_LVDS_TX1) {
|
rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
|
} else {
|
dev_err(serdes->dev, "undefined remote0_port1\n");
|
return -EINVAL;
|
}
|
}
|
|
if (serdes->version == SERDES_V1) {
|
rkx120_linkrx_engine_enable(serdes, 0, DEVICE_REMOTE0, true);
|
rkx110_linktx_channel_enable(serdes, 0, DEVICE_LOCAL, true);
|
}
|
|
rkx110_linktx_video_enable(serdes, DEVICE_LOCAL, true);
|
} else {
|
/* for camera stream */
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_route_disable(struct rk_serdes *serdes, struct rk_serdes_route *route)
|
{
|
if (route->stream_type == STREAM_DISPLAY) {
|
if (route->remote0_port0 & RK_SERDES_DSI_TX0)
|
rkx120_dsi_tx_disable(serdes, route, DEVICE_REMOTE0);
|
|
if (serdes->version == SERDES_V1) {
|
rkx120_linkrx_engine_enable(serdes, 0, DEVICE_REMOTE0, false);
|
rkx110_linktx_channel_enable(serdes, 0, DEVICE_LOCAL, false);
|
|
if (route->local_port0 == RK_SERDES_DUAL_LVDS_RX) {
|
rkx110_set_stream_source(serdes, RK_SERDES_RGB_RX,
|
DEVICE_LOCAL);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX);
|
}
|
|
if ((route->local_port0 == RK_SERDES_DSI_RX0) ||
|
(route->local_port1 == RK_SERDES_DSI_RX0)) {
|
serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
|
0x1400140);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX);
|
}
|
|
if ((route->local_port0 == RK_SERDES_DSI_RX1) ||
|
(route->local_port1 == RK_SERDES_DSI_RX1)) {
|
serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
|
0x2800280);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX);
|
hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX);
|
}
|
}
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_route_unprepare(struct rk_serdes *serdes, struct rk_serdes_route *route)
|
{
|
if (route->stream_type == STREAM_DISPLAY) {
|
if (route->remote0_port0 & RK_SERDES_DSI_TX0)
|
rkx120_dsi_tx_post_disable(serdes, route, DEVICE_REMOTE0);
|
|
if (serdes->version == SERDES_V1) {
|
if (route->local_port0 == RK_SERDES_DUAL_LVDS_RX) {
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX);
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX);
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX);
|
rkx110_set_stream_source(serdes, RK_SERDES_DUAL_LVDS_RX,
|
DEVICE_LOCAL);
|
}
|
|
if ((route->local_port0 == RK_SERDES_DSI_RX0) ||
|
(route->local_port1 == RK_SERDES_DSI_RX0)) {
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX);
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX);
|
serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
|
0x1400000);
|
}
|
|
if ((route->local_port0 == RK_SERDES_DSI_RX1) ||
|
(route->local_port1 == RK_SERDES_DSI_RX1)) {
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX);
|
hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
|
RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX);
|
serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
|
0x2800000);
|
}
|
}
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_set_hwpin(struct rk_serdes *serdes, struct i2c_client *client,
|
int pintype, int bank, uint32_t mpins, uint32_t param)
|
{
|
struct xferpin xfer;
|
char name[16];
|
|
snprintf(name, sizeof(name), "0x%x", client->addr);
|
|
xfer.name = name;
|
xfer.client = client;
|
xfer.type = pintype;
|
xfer.bank = bank;
|
xfer.mpins = mpins;
|
xfer.param = param;
|
xfer.read = serdes->i2c_read_reg;
|
xfer.write = serdes->i2c_write_reg;
|
|
return hwpin_set(xfer);
|
}
|
|
static void rk_serdes_add_callback(struct rk_serdes *serdes)
|
{
|
serdes->i2c_read_reg = rk_serdes_i2c_read;
|
serdes->i2c_write_reg = rk_serdes_i2c_write;
|
serdes->i2c_update_bits = rk_serdes_i2c_update_bits;
|
serdes->route_prepare = rk_serdes_route_prepare;
|
serdes->route_enable = rk_serdes_route_enable;
|
serdes->route_disable = rk_serdes_route_disable;
|
serdes->route_unprepare = rk_serdes_route_unprepare;
|
serdes->set_hwpin = rk_serdes_set_hwpin;
|
}
|
|
static int rk_serdes_passthrough_init(struct rk_serdes *serdes)
|
{
|
struct device_node *np;
|
u32 *configs;
|
char name[30] = "rk-serdes,pt";
|
int length, i, ret;
|
u32 devicerx_id, devicetx_id, func_id;
|
|
/* rk-serdes,passthrough = <devicerx_id devicetx_id passthrough_func>; */
|
for_each_child_of_node(serdes->dev->of_node, np) {
|
length = of_property_count_u32_elems(np, name);
|
if (length < 0)
|
continue;
|
if (length % 3) {
|
dev_err(serdes->dev, "Invalid count for passthrough %s\n", np->name);
|
return -EINVAL;
|
}
|
configs = kmalloc_array(length, sizeof(u32), GFP_KERNEL);
|
if (!configs)
|
return -ENOMEM;
|
|
ret = of_property_read_u32_array(np, name, configs, length);
|
if (ret) {
|
dev_err(serdes->dev, "get %s passthrough configs data error\n", np->name);
|
kfree(configs);
|
return -EINVAL;
|
}
|
for (i = 0; i < length; i += 3) {
|
devicerx_id = configs[i];
|
devicetx_id = configs[i + 1];
|
func_id = configs[i + 2];
|
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
if (devicerx_id == DEVICE_LOCAL) {
|
/* soc out->rkx110 in->rkx120 out->device in */
|
rkx110_linktx_passthrough_cfg(serdes, devicerx_id, func_id,
|
true);
|
rkx120_linkrx_passthrough_cfg(serdes, devicetx_id, func_id,
|
false);
|
} else {
|
/* device out->rkx120 in->rkx110 out->soc in */
|
rkx110_linktx_passthrough_cfg(serdes, devicetx_id, func_id,
|
false);
|
rkx120_linkrx_passthrough_cfg(serdes, devicerx_id, func_id,
|
true);
|
}
|
} else {
|
if (devicerx_id == DEVICE_LOCAL) {
|
/* soc out->rkx120 in->rkx110 out->device in */
|
rkx110_linktx_passthrough_cfg(serdes, devicetx_id, func_id,
|
false);
|
rkx120_linkrx_passthrough_cfg(serdes, devicerx_id, func_id,
|
true);
|
} else {
|
/* device out->rkx110 in->rkx120 out->soc in */
|
rkx110_linktx_passthrough_cfg(serdes, devicerx_id, func_id,
|
true);
|
rkx120_linkrx_passthrough_cfg(serdes, devicetx_id, func_id,
|
false);
|
}
|
}
|
dev_info(serdes->dev, "%s: devicerx_id %x, devicetx_id %x, func_id %x\n",
|
np->name, devicerx_id, devicetx_id, func_id);
|
}
|
|
kfree(configs);
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_clk_show(struct seq_file *s, void *v)
|
{
|
hwclk_dump_tree(CLK_ALL);
|
|
return 0;
|
}
|
|
static int rk_serdes_clk_open(struct inode *inode, struct file *file)
|
{
|
|
return single_open(file, rk_serdes_clk_show, NULL);
|
}
|
|
static const struct file_operations rk_serdes_clk_fops = {
|
.owner = THIS_MODULE,
|
.open = rk_serdes_clk_open,
|
.read = seq_read,
|
.llseek = seq_lseek,
|
.release = single_release,
|
};
|
|
static int rk_serdes_rate_show(struct seq_file *s, void *v)
|
{
|
struct rk_serdes *serdes = s->private;
|
|
seq_printf(s, "serdes current rate: %d\n", serdes->rate);
|
|
seq_printf(s, "%d: 2Gbps, backward: 83Mbps\n", RATE_2GBPS_83M);
|
seq_printf(s, "%d: 4Gbps, backward: 83Mbps\n", RATE_4GBPS_83M);
|
seq_printf(s, "%d: 4Gbps, backward: 125Mbps\n", RATE_4GBPS_125M);
|
seq_printf(s, "%d: 4Gbps, backward: 250Mbps\n", RATE_4GBPS_250M);
|
seq_printf(s, "%d: 4.5Gbps, backward: 140Mbps\n", RATE_4_5GBPS_140M);
|
seq_printf(s, "%d: 4.8Gbps, backward: 150Mbps\n", RATE_4_8GBPS_150M);
|
seq_printf(s, "%d: 5Gbps, backward: 156Mbps\n", RATE_5GBPS_156M);
|
// seq_printf(s, "%d: 6Gbps, backward: 187Mbps\n", RATE_6GBPS_187M);
|
|
return 0;
|
}
|
|
static ssize_t rk_serdes_rate_write(struct file *file, const char __user *buf,
|
size_t count, loff_t *ppos)
|
{
|
struct rk_serdes *serdes = file->f_path.dentry->d_inode->i_private;
|
u32 rate;
|
char kbuf[25];
|
int ret;
|
|
if (count >= sizeof(kbuf))
|
return -ENOSPC;
|
|
if (copy_from_user(kbuf, buf, count))
|
return -EFAULT;
|
|
kbuf[count] = '\0';
|
|
ret = kstrtou32(kbuf, 10, &rate);
|
if (ret < 0)
|
return ret;
|
|
rk_serdes_set_rate(serdes, rate);
|
|
return count;
|
}
|
|
static int rk_serdes_rate_open(struct inode *inode, struct file *file)
|
{
|
struct rk_serdes *serdes = inode->i_private;
|
|
return single_open(file, rk_serdes_rate_show, serdes);
|
}
|
|
static const struct file_operations rk_serdes_rate_fops = {
|
.owner = THIS_MODULE,
|
.open = rk_serdes_rate_open,
|
.read = seq_read,
|
.write = rk_serdes_rate_write,
|
.llseek = seq_lseek,
|
.release = single_release,
|
};
|
|
static void rk_serdes_function_debugfs_init(struct rk_serdes *serdes)
|
{
|
serdes->debugfs_rate = debugfs_create_file("rate", 0400, serdes->debugfs_root,
|
serdes, &rk_serdes_rate_fops);
|
serdes->debugfs_rate = debugfs_create_file("clk", 0400, serdes->debugfs_root,
|
NULL, &rk_serdes_clk_fops);
|
}
|
|
static void rk_serdes_debugfs_init(struct rk_serdes *serdes)
|
{
|
#if defined(CONFIG_DEBUG_FS)
|
serdes->debugfs_root =
|
debugfs_create_dir(dev_name(serdes->dev), debugfs_lookup("rkserdes", NULL));
|
serdes->debugfs_local = debugfs_create_dir("local", serdes->debugfs_root);
|
|
if (rk_serdes_debug_mode(serdes)) {
|
if (serdes->rkx110_debug)
|
rkx110_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
|
else
|
rkx120_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
|
} else {
|
serdes->debugfs_remote0 = debugfs_create_dir("remote0", serdes->debugfs_root);
|
serdes->debugfs_remote1 = debugfs_create_dir("remote1", serdes->debugfs_root);
|
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
rkx110_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
|
rkx120_debugfs_init(&serdes->chip[DEVICE_REMOTE0], serdes->debugfs_remote0);
|
if (serdes->remote_nr == 2)
|
rkx120_debugfs_init(&serdes->chip[DEVICE_REMOTE1],
|
serdes->debugfs_remote1);
|
} else {
|
rkx120_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
|
rkx110_debugfs_init(&serdes->chip[DEVICE_REMOTE0], serdes->debugfs_remote0);
|
if (serdes->remote_nr == 2)
|
rkx110_debugfs_init(&serdes->chip[DEVICE_REMOTE1],
|
serdes->debugfs_remote1);
|
}
|
|
rk_serdes_function_debugfs_init(serdes);
|
}
|
#endif
|
}
|
|
static void rk_serdes_read_chip_id(struct rk_serdes *serdes)
|
{
|
struct i2c_client *client;
|
int i;
|
u32 chip_id, local_id_reg, remote_id_reg, reg;
|
u32 version = 0;
|
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
local_id_reg = SER_GRF_CHIP_ID;
|
remote_id_reg = DES_GRF_CHIP_ID;
|
} else {
|
local_id_reg = DES_GRF_CHIP_ID;
|
remote_id_reg = SER_GRF_CHIP_ID;
|
}
|
|
for (i = 0; i <= serdes->remote_nr; i++) {
|
client = serdes->chip[i].client;
|
reg = i > 0 ? remote_id_reg : local_id_reg;
|
|
serdes->i2c_read_reg(client, reg, &chip_id);
|
if (i == 0)
|
version = chip_id;
|
dev_info(&client->dev, "device%d chip_id: 0x%x\n", i, chip_id);
|
}
|
|
if (version == SERDES_VERSION_V1(serdes->stream_type))
|
serdes->version = SERDES_V1;
|
else
|
serdes->version = SERDES_V0;
|
}
|
|
static struct hwclk *rk_serdes_register_hwclk(struct rk_serdes *serdes, struct i2c_client *client,
|
int idx, int clktype)
|
{
|
struct xferclk xfer;
|
char name[16];
|
|
snprintf(name, sizeof(name), "0x%x", client->addr);
|
|
xfer.name = name;
|
xfer.type = clktype;
|
xfer.client = client;
|
xfer.read = serdes->i2c_read_reg;
|
xfer.write = serdes->i2c_write_reg;
|
xfer.version = serdes->version;
|
|
return hwclk_register(xfer);
|
}
|
|
static int rk_serdes_add_hwclk(struct rk_serdes *serdes)
|
{
|
struct i2c_client *client;
|
struct hwclk *hwclk;
|
int clktype, local_clktype, remote_clktype;
|
int i;
|
|
if (serdes->stream_type == STREAM_DISPLAY) {
|
local_clktype = CLK_RKX110;
|
remote_clktype = CLK_RKX120;
|
} else {
|
local_clktype = CLK_RKX120;
|
remote_clktype = CLK_RKX110;
|
}
|
|
for (i = 0; i <= serdes->remote_nr; i++) {
|
client = serdes->chip[i].client;
|
clktype = i > 0 ? remote_clktype : local_clktype;
|
hwclk = rk_serdes_register_hwclk(serdes, client, i, clktype);
|
if (!hwclk) {
|
dev_err(serdes->dev, "Register hwclk for device%d clktype:%d failed:\n",
|
i, clktype);
|
return -EINVAL;
|
}
|
serdes->chip[i].hwclk = hwclk;
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_add_remote_i2c_device(struct rk_serdes *serdes)
|
{
|
struct i2c_client *local_client = serdes->chip[DEVICE_LOCAL].client;
|
struct i2c_client *client;
|
int ret;
|
u32 i2c_addr;
|
|
ret = of_property_read_u32(serdes->dev->of_node, "remote0-addr", &i2c_addr);
|
if (!ret) {
|
client = devm_i2c_new_dummy_device(serdes->dev, local_client->adapter, i2c_addr);
|
if (IS_ERR(client)) {
|
dev_err(serdes->dev,
|
"failed to alloc i2c client for remote0 i2c_addr:0x%x\n", i2c_addr);
|
return -PTR_ERR(client);
|
}
|
|
serdes->chip[DEVICE_REMOTE0].client = client;
|
serdes->chip[DEVICE_REMOTE0].is_remote = true;
|
serdes->chip[DEVICE_REMOTE0].serdes = serdes;
|
serdes->remote_nr++;
|
i2c_set_clientdata(client, serdes);
|
}
|
|
ret = of_property_read_u32(serdes->dev->of_node, "remote1-addr", &i2c_addr);
|
if (!ret) {
|
if (serdes->remote_nr > 0) {
|
u32 remote0_addr = serdes->chip[DEVICE_REMOTE0].client->addr;
|
|
if (i2c_addr == remote0_addr) {
|
dev_err(serdes->dev, "remote devices i2c addr must be different\n");
|
return -EINVAL;
|
}
|
}
|
|
client = devm_i2c_new_dummy_device(serdes->dev, local_client->adapter, i2c_addr);
|
if (IS_ERR(client)) {
|
dev_err(serdes->dev, "failed to alloc i2c device\n");
|
return -PTR_ERR(client);
|
}
|
|
serdes->chip[DEVICE_REMOTE1].client = client;
|
serdes->chip[DEVICE_REMOTE1].is_remote = true;
|
serdes->chip[DEVICE_REMOTE1].serdes = serdes;
|
serdes->remote_nr++;
|
i2c_set_clientdata(client, serdes);
|
}
|
|
if (serdes->remote_nr == 0)
|
return -ENODEV;
|
|
return 0;
|
}
|
|
static int rk_serdes_pinctrl_init(struct rk_serdes *serdes)
|
{
|
struct device_node *np;
|
struct i2c_client *client;
|
u32 *configs;
|
char name[20] = "rk-serdes,pins";
|
int length, i, ret;
|
u32 client_id, bank, pins, pin_configs, pin_type;
|
|
/* rk-serdes,pins = <client_id bank_id pins pin_configs>; */
|
for_each_child_of_node(serdes->dev->of_node, np) {
|
length = of_property_count_u32_elems(np, name);
|
if (length < 0)
|
continue;
|
if (length % 4) {
|
dev_err(serdes->dev, "Invalid count for pinctrl %s\n", np->name);
|
return -EINVAL;
|
}
|
configs = kmalloc_array(length, sizeof(u32), GFP_KERNEL);
|
if (!configs)
|
return -ENOMEM;
|
|
ret = of_property_read_u32_array(np, name, configs, length);
|
if (ret) {
|
dev_err(serdes->dev, "get %s configs data error\n", np->name);
|
kfree(configs);
|
return -EINVAL;
|
}
|
for (i = 0; i < length; i += 4) {
|
client_id = configs[i];
|
bank = configs[i + 1];
|
pins = configs[i + 2];
|
pin_configs = configs[i + 3];
|
pin_type = client_id ? PIN_RKX120 : PIN_RKX110;
|
client = serdes->chip[client_id].client;
|
serdes->set_hwpin(serdes, client, pin_type, bank, pins, pin_configs);
|
dev_dbg(serdes->dev, "%s: client_id %x, bank %x, pins %x pin_configs %x\n",
|
np->name, client_id, bank, pins, pin_configs);
|
}
|
|
kfree(configs);
|
}
|
|
return 0;
|
}
|
|
static int rk_serdes_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
{
|
struct device *dev = &client->dev;
|
struct rk_serdes *serdes;
|
int ret;
|
|
serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
|
if (!serdes)
|
return -ENOMEM;
|
|
serdes->dev = dev;
|
serdes->chip[DEVICE_LOCAL].client = client;
|
serdes->chip[DEVICE_LOCAL].is_remote = false;
|
serdes->chip[DEVICE_LOCAL].serdes = serdes;
|
i2c_set_clientdata(client, serdes);
|
|
rk_serdes_add_callback(serdes);
|
|
if (of_device_is_compatible(dev->of_node, "rockchip,rkx110-debug")) {
|
serdes->rkx110_debug = true;
|
dev_info(dev, "rkx110 debug mode");
|
}
|
|
if (of_device_is_compatible(dev->of_node, "rockchip,rkx120-debug")) {
|
serdes->rkx120_debug = true;
|
dev_info(dev, "rkx120 debug mode");
|
}
|
|
if (rk_serdes_debug_mode(serdes))
|
goto out;
|
|
serdes->rate = RATE_2GBPS_83M;
|
|
serdes->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
|
if (IS_ERR(serdes->enable)) {
|
ret = PTR_ERR(serdes->enable);
|
dev_err(dev, "failed to request enable GPIO: %d\n", ret);
|
return ret;
|
}
|
|
serdes->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
if (IS_ERR(serdes->reset)) {
|
ret = PTR_ERR(serdes->reset);
|
dev_err(dev, "failed to request reset GPIO: %d\n", ret);
|
return ret;
|
}
|
|
gpiod_set_value(serdes->enable, 1);
|
|
gpiod_set_value(serdes->reset, 1);
|
usleep_range(10000, 11000);
|
gpiod_set_value(serdes->reset, 0);
|
|
if (of_get_child_by_name(dev->of_node, "serdes-panel")) {
|
serdes->stream_type = STREAM_DISPLAY;
|
of_node_put(dev->of_node);
|
dev_info(dev, "serdes display stream");
|
} else {
|
serdes->stream_type = STREAM_CAMERA;
|
dev_info(dev, "serdes camera stream");
|
}
|
|
ret = rk_serdes_add_remote_i2c_device(serdes);
|
if (ret)
|
return ret;
|
|
msleep(20);
|
|
ret = mfd_add_devices(dev, -1, rkx110_x120_devs, ARRAY_SIZE(rkx110_x120_devs),
|
NULL, 0, NULL);
|
if (ret) {
|
dev_err(dev, "failed to add subdev: %d\n", ret);
|
return ret;
|
}
|
|
rk_serdes_wait_link_ready(serdes);
|
|
rk_serdes_read_chip_id(serdes);
|
|
ret = rk_serdes_add_hwclk(serdes);
|
if (ret < 0)
|
return ret;
|
|
rk_serdes_set_rate(serdes, RATE_4GBPS_83M);
|
rk_serdes_pinctrl_init(serdes);
|
rk_serdes_passthrough_init(serdes);
|
out:
|
rk_serdes_debugfs_init(serdes);
|
|
return 0;
|
}
|
|
static int rk_serdes_i2c_remove(struct i2c_client *client)
|
{
|
struct rk_serdes *rk_serdes = i2c_get_clientdata(client);
|
|
mfd_remove_devices(rk_serdes->dev);
|
|
return 0;
|
}
|
|
static const struct of_device_id rk_serdes_of_match[] = {
|
{ .compatible = "rockchip,rkx110", },
|
{ .compatible = "rockchip,rkx110-debug", },
|
{ .compatible = "rockchip,rkx120", },
|
{ .compatible = "rockchip,rkx120-debug", },
|
{}
|
};
|
MODULE_DEVICE_TABLE(of, rk_serdes_of_match);
|
|
static const struct i2c_device_id rk_serdes_i2c_id[] = {
|
{ "rk_serdes", 0 },
|
{}
|
};
|
MODULE_DEVICE_TABLE(i2c, rk_serdes_i2c_id);
|
|
static struct i2c_driver rk_serdes_i2c_driver = {
|
.driver = {
|
.name = "rk_serdes",
|
.of_match_table = of_match_ptr(rk_serdes_of_match),
|
},
|
.probe = rk_serdes_i2c_probe,
|
.remove = rk_serdes_i2c_remove,
|
.id_table = rk_serdes_i2c_id,
|
};
|
|
static int __init rk_serdes_i2c_driver_init(void)
|
{
|
debugfs_create_dir("rkserdes", NULL);
|
return i2c_add_driver(&rk_serdes_i2c_driver);
|
}
|
module_init(rk_serdes_i2c_driver_init);
|
|
static void __exit rk_serdes_i2c_driver_exit(void)
|
{
|
debugfs_remove_recursive(debugfs_lookup("rkserdes", NULL));
|
i2c_del_driver(&rk_serdes_i2c_driver);
|
}
|
module_exit(rk_serdes_i2c_driver_exit);
|
|
MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
|
MODULE_DESCRIPTION("Rockchip RK Parus MFD driver");
|
MODULE_LICENSE("GPL");
|