342 lines
8.0 KiB
C

// 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"
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,
.type = RK_SERDES_DSI_TX0,
}, {
.name = "dual-lvds",
.link_src_reg = DES_GRF_SOC_CON1,
.link_src_offset = 14,
.type = RK_SERDES_DUAL_LVDS_TX,
}, {
.name = "lvds0",
.base = RKX120_PATTERN_GEN_LVDS0_BASE,
.link_src_reg = DES_GRF_SOC_CON2,
.link_src_offset = 13,
.type = RK_SERDES_LVDS_TX0,
}, {
.name = "lvds1",
.base = RKX120_PATTERN_GEN_LVDS1_BASE,
.link_src_reg = DES_GRF_SOC_CON2,
.link_src_offset = 14,
.type = RK_SERDES_LVDS_TX1,
},
{ /* 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 = "pwm",
.reg_base = RKX120_PWM_BASE,
.reg_len = 0x100,
},
{
.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;
struct dentry *dir;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
pattern_gen = devm_kmemdup(chip->serdes->dev, &rkx120_pattern_gen,
sizeof(rkx120_pattern_gen), GFP_KERNEL);
if (!pattern_gen)
return;
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++;
}
}
}
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;
struct rk_serdes_panel *sd_panel = container_of(route, struct rk_serdes_panel, route);
struct rkx120_combtxphy *combtxphy = &sd_panel->combtxphy;
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(combtxphy, COMBTX_PHY_MODE_VIDEO_LVDS);
if (route->remote0_port0 & RK_SERDES_DUAL_LVDS_TX)
rkx120_combtxphy_set_rate(combtxphy, route->vm.pixelclock * 7 / 2);
else
rkx120_combtxphy_set_rate(combtxphy, route->vm.pixelclock * 7);
rkx120_combtxphy_power_on(serdes, combtxphy, remote_id, phy_id);
return 0;
}
int rkx120_lvds_tx_disable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
u8 phy_id)
{
struct rk_serdes_panel *sd_panel = container_of(route, struct rk_serdes_panel, route);
struct rkx120_combtxphy *combtxphy = &sd_panel->combtxphy;
rkx120_combtxphy_power_off(serdes, combtxphy, remote_id, phy_id);
return 0;
}