diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index ccebade5ff7f..d268f5502aa1 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -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);