media: rockchip: hdmirx: add interface for user to set EDID

Signed-off-by: Chen Shunqing <csq@rock-chips.com>
Change-Id: I6fd46c472a35e13db55dc67de34943e7f2194a1c
This commit is contained in:
Chen Shunqing
2022-05-16 11:33:11 +00:00
committed by Tao Huang
parent e39a6e8221
commit 4c2c5e87e1

View File

@@ -121,6 +121,12 @@ enum hdmirx_reg_attr {
HDMIRX_ATTR_RE = 3,
};
enum hdmirx_edid_version {
HDMIRX_EDID_USER = 0,
HDMIRX_EDID_340M = 1,
HDMIRX_EDID_600M = 2,
};
struct hdmirx_reg_table {
int reg_base;
int reg_end;
@@ -195,6 +201,7 @@ struct rk_hdmirx_dev {
struct regmap *vo1_grf;
struct rk_hdmirx_hdcp *hdcp;
void __iomem *regs;
int edid_version;
int audio_present;
int hdmi_irq;
int dma_irq;
@@ -220,6 +227,7 @@ struct rk_hdmirx_dev {
u32 cpu_freq_khz;
u32 bound_cpu;
u32 wdt_cfg_bound_cpu;
u8 edid[EDID_BLOCK_SIZE * 2];
hdmi_codec_plugged_cb plugged_cb;
spinlock_t dma_rst_lock;
};
@@ -234,41 +242,78 @@ static void hdmirx_audio_handle_plugged_change(struct rk_hdmirx_dev *hdmirx_dev,
static void hdmirx_audio_interrupts_setup(struct rk_hdmirx_dev *hdmirx_dev, bool en);
static int hdmirx_set_cpu_limit_freq(struct rk_hdmirx_dev *hdmirx_dev);
static void hdmirx_cancel_cpu_limit_freq(struct rk_hdmirx_dev *hdmirx_dev);
static void hdmirx_plugout(struct rk_hdmirx_dev *hdmirx_dev);
static u8 edid_init_data[] = {
static u8 edid_init_data_340M[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x49, 0x70, 0x88, 0x35, 0x01, 0x00, 0x00, 0x00,
0x2D, 0x1F, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40,
0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xE8,
0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58,
0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40,
0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E,
0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20,
0x6E, 0x28, 0x55, 0x00, 0x20, 0xC2, 0x31, 0x00,
0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52,
0x4B, 0x2D, 0x55, 0x48, 0x44, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA3,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA7,
0x02, 0x03, 0x36, 0xD2, 0x51, 0x07, 0x16, 0x14,
0x02, 0x03, 0x2F, 0xD1, 0x51, 0x07, 0x16, 0x14,
0x05, 0x01, 0x03, 0x12, 0x13, 0x84, 0x22, 0x1F,
0x90, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x23, 0x09,
0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x67, 0x03,
0x0C, 0x00, 0x30, 0x00, 0x10, 0x44, 0xE3, 0x05,
0x03, 0x01, 0xE4, 0x0F, 0x00, 0x80, 0x01, 0x02,
0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58,
0x2C, 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
};
static u8 edid_init_data_600M[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x49, 0x70, 0x88, 0x35, 0x01, 0x00, 0x00, 0x00,
0x2D, 0x1F, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
0x09, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xE8,
0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58,
0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
0x08, 0xE8, 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80,
0xB0, 0x58, 0x8A, 0x00, 0x20, 0xC2, 0x31, 0x00,
0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52,
0x4B, 0x2D, 0x55, 0x48, 0x44, 0x0A, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x39,
0x02, 0x03, 0x21, 0xD2, 0x41, 0x61, 0x23, 0x09,
0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x66, 0x03,
0x0C, 0x00, 0x30, 0x00, 0x10, 0x67, 0xD8, 0x5D,
0xC4, 0x01, 0x78, 0xC0, 0x07, 0xE3, 0x05, 0x03,
0x01, 0xE4, 0x0F, 0x00, 0xF0, 0x01, 0x08, 0xE8,
0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58,
0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40,
0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, 0x00,
0x00, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x08, 0xE8, 0x00, 0x30, 0xF2, 0x70, 0x5A,
0x80, 0xB0, 0x58, 0x8A, 0x00, 0xC4, 0x8E, 0x21,
0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8,
};
static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
@@ -817,6 +862,7 @@ static int hdmirx_write_edid(struct rk_hdmirx_dev *hdmirx_dev,
return 0;
}
memset(&hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
hdmirx_hpd_ctrl(hdmirx_dev, false);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
EDID_READ_EN_MASK |
@@ -857,6 +903,7 @@ static int hdmirx_write_edid(struct rk_hdmirx_dev *hdmirx_dev,
EDID_WRITE_EN(0));
hdmirx_dev->edid_blocks_written = edid->blocks;
memcpy(&hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
if (hpd_up) {
if (tx_5v_power_present(hdmirx_dev))
hdmirx_hpd_ctrl(hdmirx_dev, true);
@@ -871,7 +918,23 @@ static int hdmirx_set_edid(struct file *file, void *fh,
struct hdmirx_stream *stream = video_drvdata(file);
struct rk_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
return hdmirx_write_edid(hdmirx_dev, edid, true);
disable_irq(hdmirx_dev->hdmi_irq);
disable_irq(hdmirx_dev->dma_irq);
sip_fiq_control(RK_SIP_FIQ_CTRL_FIQ_DIS, RK_IRQ_HDMIRX_HDMI, 0);
if (tx_5v_power_present(hdmirx_dev))
hdmirx_plugout(hdmirx_dev);
hdmirx_write_edid(hdmirx_dev, edid, false);
hdmirx_dev->edid_version = HDMIRX_EDID_USER;
enable_irq(hdmirx_dev->hdmi_irq);
enable_irq(hdmirx_dev->dma_irq);
sip_fiq_control(RK_SIP_FIQ_CTRL_FIQ_EN, RK_IRQ_HDMIRX_HDMI, 0);
schedule_delayed_work_on(hdmirx_dev->bound_cpu,
&hdmirx_dev->delayed_work_hotplug,
msecs_to_jiffies(200));
return 0;
}
static int hdmirx_get_edid(struct file *file, void *fh,
@@ -880,7 +943,6 @@ static int hdmirx_get_edid(struct file *file, void *fh,
struct hdmirx_stream *stream = video_drvdata(file);
struct rk_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
u32 i;
memset(edid->reserved, 0, sizeof(edid->reserved));
@@ -902,26 +964,13 @@ static int hdmirx_get_edid(struct file *file, void *fh,
if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
EDID_READ_EN_MASK |
EDID_WRITE_EN_MASK,
EDID_READ_EN(1) |
EDID_WRITE_EN(0));
for (i = 0; i < (edid->blocks * EDID_BLOCK_SIZE); i++)
edid->edid[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
memcpy(edid->edid, &hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
v4l2_dbg(1, debug, v4l2_dev, "%s: Read EDID: =====\n", __func__);
if (debug > 0)
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
EDID_READ_EN_MASK |
EDID_WRITE_EN_MASK,
EDID_READ_EN(0) |
EDID_WRITE_EN(0));
return 0;
}
@@ -2308,6 +2357,56 @@ static void hdmirx_interrupts_setup(struct rk_hdmirx_dev *hdmirx_dev, bool en)
}
}
static void hdmirx_plugin(struct rk_hdmirx_dev *hdmirx_dev)
{
cpu_latency_qos_update_request(&hdmirx_dev->pm_qos, 0);
schedule_delayed_work_on(hdmirx_dev->bound_cpu,
&hdmirx_dev->delayed_work_heartbeat, msecs_to_jiffies(10));
sip_wdt_config(WDT_START, 0, 0, 0);
hdmirx_set_cpu_limit_freq(hdmirx_dev);
hdmirx_submodule_init(hdmirx_dev);
hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
POWERPROVIDED);
hdmirx_hpd_ctrl(hdmirx_dev, true);
hdmirx_phy_config(hdmirx_dev);
hdmirx_audio_setup(hdmirx_dev);
hdmirx_wait_lock_and_get_timing(hdmirx_dev);
hdmirx_dma_config(hdmirx_dev);
hdmirx_interrupts_setup(hdmirx_dev, true);
hdmirx_audio_handle_plugged_change(hdmirx_dev, 1);
if (hdmirx_dev->hdcp && hdmirx_dev->hdcp->hdcp_start)
hdmirx_dev->hdcp->hdcp_start(hdmirx_dev->hdcp);
}
static void hdmirx_plugout(struct rk_hdmirx_dev *hdmirx_dev)
{
hdmirx_audio_handle_plugged_change(hdmirx_dev, 0);
hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
hdmirx_interrupts_setup(hdmirx_dev, false);
hdmirx_hpd_ctrl(hdmirx_dev, false);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
LINE_FLAG_INT_EN |
HDMIRX_DMA_IDLE_INT |
HDMIRX_LOCK_DISABLE_INT |
LAST_FRAME_AXI_UNFINISH_INT_EN |
FIFO_OVERFLOW_INT_EN |
FIFO_UNDERFLOW_INT_EN |
HDMIRX_AXI_ERROR_INT_EN, 0);
hdmirx_reset_dma(hdmirx_dev);
hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
HDMI_DISABLE | PHY_RESET | PHY_PDDQ,
HDMI_DISABLE);
hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
cancel_delayed_work(&hdmirx_dev->delayed_work_audio);
cpu_latency_qos_update_request(&hdmirx_dev->pm_qos, PM_QOS_DEFAULT_VALUE);
hdmirx_cancel_cpu_limit_freq(hdmirx_dev);
cancel_delayed_work(&hdmirx_dev->delayed_work_heartbeat);
flush_work(&hdmirx_dev->work_wdt_config);
sip_wdt_config(WDT_STOP, 0, 0, 0);
}
static void hdmirx_delayed_work_hotplug(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -2322,51 +2421,11 @@ static void hdmirx_delayed_work_hotplug(struct work_struct *work)
v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
v4l2_dbg(1, debug, v4l2_dev, "%s: plugin:%d\n", __func__, plugin);
if (plugin) {
cpu_latency_qos_update_request(&hdmirx_dev->pm_qos, 0);
schedule_delayed_work_on(hdmirx_dev->bound_cpu,
&hdmirx_dev->delayed_work_heartbeat, msecs_to_jiffies(10));
sip_wdt_config(WDT_START, 0, 0, 0);
hdmirx_set_cpu_limit_freq(hdmirx_dev);
hdmirx_submodule_init(hdmirx_dev);
hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
POWERPROVIDED);
hdmirx_hpd_ctrl(hdmirx_dev, true);
hdmirx_phy_config(hdmirx_dev);
hdmirx_audio_setup(hdmirx_dev);
hdmirx_wait_lock_and_get_timing(hdmirx_dev);
hdmirx_dma_config(hdmirx_dev);
hdmirx_interrupts_setup(hdmirx_dev, true);
hdmirx_audio_handle_plugged_change(hdmirx_dev, 1);
if (hdmirx_dev->hdcp && hdmirx_dev->hdcp->hdcp_start)
hdmirx_dev->hdcp->hdcp_start(hdmirx_dev->hdcp);
} else {
hdmirx_audio_handle_plugged_change(hdmirx_dev, 0);
hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
hdmirx_interrupts_setup(hdmirx_dev, false);
hdmirx_hpd_ctrl(hdmirx_dev, false);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
LINE_FLAG_INT_EN |
HDMIRX_DMA_IDLE_INT |
HDMIRX_LOCK_DISABLE_INT |
LAST_FRAME_AXI_UNFINISH_INT_EN |
FIFO_OVERFLOW_INT_EN |
FIFO_UNDERFLOW_INT_EN |
HDMIRX_AXI_ERROR_INT_EN, 0);
hdmirx_reset_dma(hdmirx_dev);
hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
HDMI_DISABLE | PHY_RESET | PHY_PDDQ,
HDMI_DISABLE);
hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
cancel_delayed_work(&hdmirx_dev->delayed_work_audio);
cpu_latency_qos_update_request(&hdmirx_dev->pm_qos, PM_QOS_DEFAULT_VALUE);
hdmirx_cancel_cpu_limit_freq(hdmirx_dev);
cancel_delayed_work(&hdmirx_dev->delayed_work_heartbeat);
flush_work(&hdmirx_dev->work_wdt_config);
sip_wdt_config(WDT_STOP, 0, 0, 0);
}
if (plugin)
hdmirx_plugin(hdmirx_dev);
else
hdmirx_plugout(hdmirx_dev);
mutex_unlock(&hdmirx_dev->work_lock);
}
@@ -2886,7 +2945,10 @@ static void hdmirx_edid_init_config(struct rk_hdmirx_dev *hdmirx_dev)
def_edid.pad = 0;
def_edid.start_block = 0;
def_edid.blocks = EDID_NUM_BLOCKS_MAX;
def_edid.edid = edid_init_data;
if (hdmirx_dev->edid_version == HDMIRX_EDID_600M)
def_edid.edid = edid_init_data_600M;
else
def_edid.edid = edid_init_data_340M;
ret = hdmirx_write_edid(hdmirx_dev, &def_edid, false);
if (ret)
dev_err(hdmirx_dev->dev, "%s write edid failed!\n", __func__);
@@ -2989,12 +3051,63 @@ static ssize_t audio_present_show(struct device *dev,
tx_5v_power_present(hdmirx_dev) ? hdmirx_dev->audio_present : 0);
}
static ssize_t edid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rk_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
int edid = 0;
if (hdmirx_dev)
edid = hdmirx_dev->edid_version;
return snprintf(buf, PAGE_SIZE, "%d\n", edid);
}
static ssize_t edid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int edid;
struct rk_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
if (!hdmirx_dev)
return -EINVAL;
if (kstrtoint(buf, 10, &edid))
return -EINVAL;
if (edid != HDMIRX_EDID_340M && edid != HDMIRX_EDID_600M)
return count;
if (hdmirx_dev->edid_version != edid) {
disable_irq(hdmirx_dev->hdmi_irq);
disable_irq(hdmirx_dev->dma_irq);
sip_fiq_control(RK_SIP_FIQ_CTRL_FIQ_DIS, RK_IRQ_HDMIRX_HDMI, 0);
if (tx_5v_power_present(hdmirx_dev))
hdmirx_plugout(hdmirx_dev);
hdmirx_dev->edid_version = edid;
hdmirx_edid_init_config(hdmirx_dev);
enable_irq(hdmirx_dev->hdmi_irq);
enable_irq(hdmirx_dev->dma_irq);
sip_fiq_control(RK_SIP_FIQ_CTRL_FIQ_EN, RK_IRQ_HDMIRX_HDMI, 0);
schedule_delayed_work_on(hdmirx_dev->bound_cpu,
&hdmirx_dev->delayed_work_hotplug,
msecs_to_jiffies(200));
}
return count;
}
static DEVICE_ATTR_RO(audio_rate);
static DEVICE_ATTR_RO(audio_present);
static DEVICE_ATTR_RW(edid);
static struct attribute *hdmirx_attrs[] = {
&dev_attr_audio_rate.attr,
&dev_attr_audio_present.attr,
&dev_attr_edid.attr,
NULL
};
ATTRIBUTE_GROUPS(hdmirx);
@@ -3458,6 +3571,7 @@ static int hdmirx_probe(struct platform_device *pdev)
hdmirx_dev->dev = dev;
hdmirx_dev->of_node = dev->of_node;
hdmirx_dev->cpu_freq_khz = CPU_LIMIT_FREQ_KHZ;
hdmirx_dev->edid_version = HDMIRX_EDID_340M;
ret = hdmirx_parse_dt(hdmirx_dev);
if (ret)
return ret;
@@ -3632,7 +3746,7 @@ static int hdmirx_probe(struct platform_device *pdev)
cec_data.dev = hdmirx_dev->dev;
cec_data.ops = &hdmirx_cec_ops;
cec_data.irq = irq;
cec_data.edid = edid_init_data;
cec_data.edid = edid_init_data_340M;
hdmirx_dev->cec = rk_hdmirx_cec_register(&cec_data);
hdmirx_register_hdcp(dev, hdmirx_dev, hdmirx_dev->hdcp1x_enable);