374 lines
11 KiB
C
374 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 Rockchip Electronics Co. Ltd.
|
|
*
|
|
* Author: Guochun Huang <hero.huang@rock-chips.com>
|
|
*/
|
|
|
|
#include "rk628.h"
|
|
#include "rk628_cru.h"
|
|
#include "rk628_rgb.h"
|
|
#include "panel.h"
|
|
|
|
int rk628_rgbrx_parse(struct rk628 *rk628)
|
|
{
|
|
/* input/output: bt1120 */
|
|
if ((rk628_input_is_bt1120(rk628) || rk628_output_is_bt1120(rk628)) &&
|
|
dev_read_bool(rk628->dev, "bt1120-dual-edge"))
|
|
rk628->rgb.bt1120_dual_edge = true;
|
|
|
|
/* input: bt1120 */
|
|
if (rk628_input_is_bt1120(rk628)) {
|
|
if (dev_read_bool(rk628->dev, "bt1120-yc-swap"))
|
|
rk628->rgb.bt1120_yc_swap = true;
|
|
|
|
if (dev_read_bool(rk628->dev, "bt1120-uv-swap"))
|
|
rk628->rgb.bt1120_uv_swap = true;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rk628_rgbtx_parse(struct rk628 *rk628, ofnode rgb_np)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* input/output: rgb/bt1120 */
|
|
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, rk628->dev,
|
|
"vccio-rgb", &rk628->rgb.vccio_rgb);
|
|
if (ret && ret != -ENOENT)
|
|
return ret;
|
|
|
|
/* output: rgb/bt1120 */
|
|
if (rk628_output_is_bt1120(rk628) || rk628_output_is_rgb(rk628))
|
|
ret = rk628_panel_info_get(rk628, rgb_np);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rk628_rgb_decoder_enable(struct rk628 *rk628)
|
|
{
|
|
/* config sw_input_mode RGB */
|
|
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK,
|
|
SW_INPUT_MODE(INPUT_MODE_RGB));
|
|
|
|
if (rk628->version == RK628F_VERSION) {
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS0,
|
|
RGB_RX_MODET_EN | RGB_RX_DCLK_EN,
|
|
RGB_RX_MODET_EN | RGB_RX_DCLK_EN);
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS3,
|
|
RGB_RX_CNT_EN_MASK, RGB_RX_CNT_EN(1));
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
|
|
}
|
|
|
|
/* pinctrl for vop pin */
|
|
rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
|
|
rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
|
|
}
|
|
|
|
static void rk628_rgb_encoder_enable(struct rk628 *rk628)
|
|
{
|
|
int voltage = 0;
|
|
u32 d_strength, clk_strength;
|
|
u64 dclk_delay;
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
|
|
SW_BT_DATA_OEN_MASK | SW_OUTPUT_RGB_MODE_MASK,
|
|
SW_OUTPUT_RGB_MODE(OUTPUT_MODE_RGB >> 3));
|
|
|
|
if (rk628->version != RK628F_VERSION)
|
|
rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
|
|
SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN);
|
|
|
|
/* pinctrl for vop pin */
|
|
rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
|
|
rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
|
|
|
|
/*
|
|
* Under the same drive strength and DCLK delay, the signal behaves
|
|
* differently under different voltage power domains. In order to
|
|
* center the eye diagram of the signal, have sufficient signal setup
|
|
* and hold time, and ensure that the signal does not overshoot, the
|
|
* drive strength and DCLK delay need to be set for the power domains
|
|
* of different voltages.
|
|
*/
|
|
if (rk628->rgb.vccio_rgb)
|
|
voltage = regulator_get_value(rk628->rgb.vccio_rgb);
|
|
|
|
switch (voltage) {
|
|
case 1800000:
|
|
d_strength = 3;
|
|
clk_strength = 3;
|
|
dclk_delay = 0x10000000;
|
|
break;
|
|
case 3300000:
|
|
d_strength = 1;
|
|
clk_strength = 2;
|
|
dclk_delay = 0x100000000;
|
|
break;
|
|
default:
|
|
d_strength = 1;
|
|
clk_strength = 2;
|
|
dclk_delay = 0x100000000;
|
|
}
|
|
|
|
/* rk628: modify IO drive strength for RGB */
|
|
if (rk628->version == RK628F_VERSION)
|
|
d_strength = d_strength * 0x1111 | 0xffff0000;
|
|
else {
|
|
d_strength = 0xffff7777;
|
|
clk_strength = 7;
|
|
}
|
|
|
|
rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, d_strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, d_strength & 0xf0fff0ff);
|
|
rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, clk_strength | 0x000f0000);
|
|
|
|
/* rk628: modify DCLK delay for RGB */
|
|
if (rk628->version == RK628F_VERSION) {
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0,
|
|
dclk_delay & 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1,
|
|
dclk_delay >> 32);
|
|
}
|
|
}
|
|
|
|
static void rk628_rgb_encoder_disable(struct rk628 *rk628)
|
|
{
|
|
}
|
|
|
|
|
|
void rk628_rgb_rx_enable(struct rk628 *rk628)
|
|
{
|
|
|
|
rk628_rgb_decoder_enable(rk628);
|
|
|
|
}
|
|
|
|
void rk628_rgb_tx_enable(struct rk628 *rk628)
|
|
{
|
|
rk628_rgb_encoder_enable(rk628);
|
|
|
|
rk628_panel_prepare(rk628);
|
|
rk628_panel_enable(rk628);
|
|
}
|
|
|
|
void rk628_rgb_tx_disable(struct rk628 *rk628)
|
|
{
|
|
rk628_panel_disable(rk628);
|
|
rk628_panel_unprepare(rk628);
|
|
|
|
rk628_rgb_encoder_disable(rk628);
|
|
}
|
|
|
|
static void rk628_bt1120_decoder_timing_cfg(struct rk628 *rk628)
|
|
{
|
|
u32 src_hsync_len, src_hback_porch, src_hfront_porch, src_hactive;
|
|
u32 src_vsync_len, src_vback_porch, src_vfront_porch, src_vactive;
|
|
u32 dsp_htotal, dsp_hs_end, dsp_hact_st;
|
|
u32 dsp_vtotal, dsp_vs_end, dsp_vact_st;
|
|
u32 dsp_hbor_st, dsp_vbor_st;
|
|
u16 bor_left = 0, bor_up = 0;
|
|
struct drm_display_mode *src = &rk628->src_mode;
|
|
|
|
src_hactive = src->hdisplay;
|
|
src_hsync_len = src->hsync_end - src->hsync_start;
|
|
src_hback_porch = src->htotal - src->hsync_end;
|
|
src_hfront_porch = src->hsync_start - src->hdisplay;
|
|
src_vsync_len = src->vsync_end - src->vsync_start;
|
|
src_vback_porch = src->vtotal - src->vsync_end;
|
|
src_vfront_porch = src->vsync_start - src->vdisplay;
|
|
src_vactive = src->vdisplay;
|
|
|
|
dsp_htotal = src_hsync_len + src_hback_porch +
|
|
src_hactive + src_hfront_porch;
|
|
dsp_vtotal = src_vsync_len + src_vback_porch +
|
|
src_vactive + src_vfront_porch;
|
|
dsp_hs_end = src_hsync_len;
|
|
dsp_vs_end = src_vsync_len;
|
|
dsp_hbor_st = src_hsync_len + src_hback_porch;
|
|
dsp_vbor_st = src_vsync_len + src_vback_porch;
|
|
dsp_hact_st = dsp_hbor_st + bor_left;
|
|
dsp_vact_st = dsp_vbor_st + bor_up;
|
|
|
|
if (rk628->version == RK628F_VERSION)
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_RX_DBG_MEAS0,
|
|
RGB_RX_MODET_EN | RGB_RX_DCLK_EN,
|
|
RGB_RX_MODET_EN | RGB_RX_DCLK_EN);
|
|
|
|
rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL0, BT1120_DSP_HS_END(dsp_hs_end) |
|
|
BT1120_DSP_HTOTAL(dsp_htotal));
|
|
rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL1, BT1120_DSP_HACT_ST(dsp_hact_st));
|
|
rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL2, BT1120_DSP_VS_END(dsp_vs_end) |
|
|
BT1120_DSP_VTOTAL(dsp_vtotal));
|
|
rk628_i2c_write(rk628, GRF_BT1120_TIMING_CTRL3, BT1120_DSP_VACT_ST(dsp_vact_st));
|
|
}
|
|
|
|
static void rk628_bt1120_decoder_enable(struct rk628 *rk628)
|
|
{
|
|
struct drm_display_mode *mode = &rk628->src_mode;
|
|
unsigned long dec_clk_rate;
|
|
|
|
rk628_set_input_bus_format(rk628, BUS_FMT_YUV422);
|
|
|
|
/* pinctrl for vop pin */
|
|
rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
|
|
rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
|
|
|
|
/* config sw_input_mode bt1120 */
|
|
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK,
|
|
SW_INPUT_MODE(INPUT_MODE_BT1120));
|
|
|
|
if (rk628->version == RK628F_VERSION)
|
|
rk628_bt1120_decoder_timing_cfg(rk628);
|
|
|
|
/* operation resetn_bt1120dec */
|
|
rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10001000);
|
|
rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10000000);
|
|
|
|
/*
|
|
* BT1120 dec clk is a 4-bit integer division, which is inaccurate in
|
|
* most resolutions. So if the frequency division is not accurate, apply
|
|
* for a fault tolerance of up 2% in frequency setting, so that the
|
|
* obtained frequency is slightly higher than the actual required clk,
|
|
* so that the deviation between the actual clk and the required clk
|
|
* frequency is not significant.
|
|
*/
|
|
rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1000);
|
|
dec_clk_rate = rk628_cru_clk_get_rate(rk628, CGU_BT1120DEC);
|
|
if (dec_clk_rate < mode->clock * 1000)
|
|
rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1020);
|
|
|
|
if (rk628->rgb.bt1120_dual_edge) {
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0,
|
|
DEC_DUALEDGE_EN, DEC_DUALEDGE_EN);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
|
|
} else {
|
|
if (rk628->version == RK628F_VERSION) {
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0,
|
|
0x10000);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
|
|
}
|
|
}
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON1, SW_SET_X_MASK,
|
|
SW_SET_X(mode->hdisplay));
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON2, SW_SET_Y_MASK,
|
|
SW_SET_Y(mode->vdisplay));
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
|
|
SW_BT_DATA_OEN_MASK | SW_INPUT_MODE_MASK,
|
|
SW_BT_DATA_OEN | SW_INPUT_MODE(INPUT_MODE_BT1120));
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0,
|
|
SW_CAP_EN_PSYNC | SW_CAP_EN_ASYNC |
|
|
SW_PROGRESS_EN |
|
|
SW_BT1120_YC_SWAP |
|
|
SW_BT1120_UV_SWAP,
|
|
SW_CAP_EN_PSYNC | SW_CAP_EN_ASYNC |
|
|
SW_PROGRESS_EN |
|
|
(rk628->rgb.bt1120_yc_swap ? SW_BT1120_YC_SWAP : 0) |
|
|
(rk628->rgb.bt1120_uv_swap ? SW_BT1120_UV_SWAP : 0));
|
|
}
|
|
|
|
static void rk628_bt1120_encoder_enable(struct rk628 *rk628)
|
|
{
|
|
u32 val = 0;
|
|
int voltage = 0;
|
|
u32 strength;
|
|
u64 dclk_delay;
|
|
|
|
rk628_set_output_bus_format(rk628, BUS_FMT_YUV422);
|
|
|
|
/* pinctrl for vop pin */
|
|
rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
|
|
rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
|
|
SW_BT_DATA_OEN_MASK | SW_OUTPUT_RGB_MODE_MASK,
|
|
SW_OUTPUT_RGB_MODE(OUTPUT_MODE_BT1120 >> 3));
|
|
|
|
if (rk628->version != RK628F_VERSION)
|
|
rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
|
|
SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN);
|
|
|
|
/*
|
|
* Under the same drive strength and DCLK delay, the signal behaves
|
|
* differently under different voltage power domains. In order to
|
|
* center the eye diagram of the signal, have sufficient signal setup
|
|
* and hold time, and ensure that the signal does not overshoot, the
|
|
* drive strength and DCLK delay need to be set for the power domains
|
|
* of different voltages.
|
|
*/
|
|
if (rk628->rgb.vccio_rgb)
|
|
voltage = regulator_get_value(rk628->rgb.vccio_rgb);
|
|
|
|
switch (voltage) {
|
|
case 1800000:
|
|
strength = 3;
|
|
dclk_delay = 0x100000000;
|
|
break;
|
|
case 3300000:
|
|
strength = 1;
|
|
dclk_delay = 0x1000000000;
|
|
break;
|
|
default:
|
|
strength = 1;
|
|
dclk_delay = 0x1000000000;
|
|
}
|
|
|
|
/* rk628: modify IO drive strength for BT1120 */
|
|
if (rk628->version == RK628F_VERSION)
|
|
strength = strength * 0x1111 | 0xffff0000;
|
|
else
|
|
strength = 0xffff1111;
|
|
|
|
rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, strength);
|
|
rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, strength & 0xf0fff0ff);
|
|
rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, strength & 0x000f000f);
|
|
|
|
/* rk628: modify DCLK delay for BT1120 */
|
|
if (rk628->rgb.bt1120_dual_edge) {
|
|
val |= ENC_DUALEDGE_EN(1);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
|
|
} else {
|
|
if (rk628->version == RK628F_VERSION) {
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0,
|
|
dclk_delay & 0xffffffff);
|
|
rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1,
|
|
dclk_delay >> 32);
|
|
}
|
|
}
|
|
|
|
val |= BT1120_UV_SWAP(1);
|
|
rk628_i2c_write(rk628, GRF_RGB_ENC_CON, val);
|
|
}
|
|
|
|
void rk628_bt1120_rx_enable(struct rk628 *rk628)
|
|
{
|
|
rk628_bt1120_decoder_enable(rk628);
|
|
}
|
|
|
|
void rk628_bt1120_tx_enable(struct rk628 *rk628)
|
|
{
|
|
rk628_bt1120_encoder_enable(rk628);
|
|
}
|