412 lines
13 KiB
C
412 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* Author: Guochun Huang <hero.huang@rock-chips.com>
|
|
*/
|
|
|
|
#include <linux/debugfs.h>
|
|
#include "rk628.h"
|
|
#include "rk628_cru.h"
|
|
#include "rk628_config.h"
|
|
#include "panel.h"
|
|
#include "rk628_rgb.h"
|
|
|
|
int rk628_rgb_parse(struct rk628 *rk628, struct device_node *rgb_np)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* input/output: rgb/bt1120 */
|
|
rk628->rgb.vccio_rgb = devm_regulator_get_optional(rk628->dev, "vccio-rgb");
|
|
if (IS_ERR(rk628->rgb.vccio_rgb))
|
|
rk628->rgb.vccio_rgb = NULL;
|
|
|
|
/* input/output: bt1120 */
|
|
if ((rk628_input_is_bt1120(rk628) || rk628_output_is_bt1120(rk628))) {
|
|
if (of_property_read_bool(rk628->dev->of_node, "bt1120-dual-edge"))
|
|
rk628->rgb.bt1120_dual_edge = true;
|
|
|
|
if (of_property_read_bool(rk628->dev->of_node, "bt1120-yc-swap"))
|
|
rk628->rgb.bt1120_yc_swap = true;
|
|
|
|
if (of_property_read_bool(rk628->dev->of_node, "bt1120-uv-swap"))
|
|
rk628->rgb.bt1120_uv_swap = true;
|
|
}
|
|
|
|
/* 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 int rk628_rgb_resolution_show(struct seq_file *s, void *data)
|
|
{
|
|
struct rk628 *rk628 = s->private;
|
|
u16 width = 0, height = 0;
|
|
u32 rgb_rx_eval_time, rgb_rx_clkrate;
|
|
u64 ref_clk, pixel_clk;
|
|
u32 val;
|
|
|
|
rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS0, &val);
|
|
rgb_rx_eval_time = (val & RGB_RX_EVAL_TIME_MASK) >> 16;
|
|
|
|
rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS2, &val);
|
|
rgb_rx_clkrate = val & RGB_RX_CLKRATE_MASK;
|
|
|
|
ref_clk = rk628_cru_clk_get_rate(rk628, CGU_CLK_IMODET);
|
|
pixel_clk = ref_clk * rgb_rx_clkrate;
|
|
do_div(pixel_clk, rgb_rx_eval_time + 1);
|
|
|
|
if (rk628_input_is_rgb(rk628)) {
|
|
rk628_i2c_read(rk628, GRF_RGB_RX_DBG_MEAS4, &val);
|
|
height = (val >> 16) & 0xffff;
|
|
width = val & 0xffff;
|
|
} else if (rk628_input_is_bt1120(rk628)) {
|
|
rk628_i2c_read(rk628, GRF_SYSTEM_STATUS3, &val);
|
|
height = val & DECODER_1120_LAST_LINE_NUM_MASK;
|
|
|
|
rk628_i2c_read(rk628, GRF_SYSTEM_STATUS4, &val);
|
|
width = val & DECODER_1120_LAST_PIX_NUM_MASK;
|
|
}
|
|
|
|
seq_printf(s, "%dx%d pclk:%llu\n", width, height, pixel_clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk628_rgb_resolution_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, rk628_rgb_resolution_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations rk628_rgb_resolution_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rk628_rgb_resolution_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
void rk628_rgb_decoder_create_debugfs_file(struct rk628 *rk628)
|
|
{
|
|
if (rk628->version == RK628D_VERSION)
|
|
return;
|
|
|
|
if (rk628_input_is_rgb(rk628) || rk628_input_is_bt1120(rk628))
|
|
debugfs_create_file("rgb_resolution", 0400, rk628->debug_dir,
|
|
rk628, &rk628_rgb_resolution_fops);
|
|
}
|
|
|
|
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, 0x10000000);
|
|
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_voltage(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 rk628_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 rk628_display_mode *mode = rk628_display_get_src_mode(rk628);
|
|
|
|
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);
|
|
|
|
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,
|
|
0x08000000);
|
|
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_voltage(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 |= rk628->rgb.bt1120_yc_swap ? BT1120_YC_SWAP(1) : BT1120_YC_SWAP(0);
|
|
val |= rk628->rgb.bt1120_uv_swap ? BT1120_UV_SWAP(1) : BT1120_UV_SWAP(0);
|
|
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);
|
|
}
|