314 lines
7.1 KiB
C
314 lines
7.1 KiB
C
// 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"
|
|
|
|
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,
|
|
.type = RK_SERDES_DSI_RX0,
|
|
}, {
|
|
.name = "dsi1",
|
|
.base = RKX110_PATTERN_GEN_DSI1_BASE,
|
|
.link_src_reg = SER_GRF_SOC_CON4,
|
|
.link_src_offset = 13,
|
|
.type = RK_SERDES_DSI_RX1,
|
|
}, {
|
|
.name = "dual-lvds",
|
|
.link_src_reg = SER_GRF_SOC_CON1,
|
|
.link_src_offset = 14,
|
|
.type = RK_SERDES_DUAL_LVDS_RX,
|
|
}, {
|
|
.name = "lvds0",
|
|
.base = RKX110_PATTERN_GEN_LVDS0_BASE,
|
|
.link_src_reg = SER_GRF_SOC_CON4,
|
|
.link_src_offset = 14,
|
|
.type = RK_SERDES_LVDS_RX0,
|
|
}, {
|
|
.name = "lvds1",
|
|
.base = RKX110_PATTERN_GEN_LVDS1_BASE,
|
|
.link_src_reg = SER_GRF_SOC_CON4,
|
|
.link_src_offset = 15,
|
|
.type = RK_SERDES_LVDS_RX1,
|
|
},
|
|
{ /* 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct rk_serdes_panel *sd_panel = container_of(route, struct rk_serdes_panel, route);
|
|
struct rkx110_combrxphy *combrxphy = &sd_panel->combrxphy;
|
|
|
|
rkx110_combrxphy_set_mode(combrxphy, COMBRX_PHY_MODE_VIDEO_LVDS);
|
|
rkx110_combrxphy_power_on(serdes, combrxphy, DEVICE_LOCAL, id ? COMBPHY_1 : COMBPHY_0);
|
|
|
|
return 0;
|
|
}
|
|
|