From 6c02dc64a4fdcf7185edbf1e429584b3b864a8fd Mon Sep 17 00:00:00 2001 From: Sandor Yu Date: Fri, 14 Sep 2018 13:26:15 +0800 Subject: [PATCH] MLK-19583-1: hdmi rx: Add HPD support Add HPD support. Add s_param function that may needed by user application. Add mutex protect for hdp register access. Signed-off-by: Sandor Yu --- .../media/platform/imx8/hdmi/mxc-hdmi-hw.c | 219 ++++++++++++------ .../media/platform/imx8/hdmi/mxc-hdmi-rx.c | 183 +++++++++++---- .../media/platform/imx8/hdmi/mxc-hdmi-rx.h | 18 +- 3 files changed, 304 insertions(+), 116 deletions(-) diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c index d8ebd019c994..a6f582c5373d 100644 --- a/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c @@ -67,93 +67,111 @@ S_HDMI_SCDC_SET_MSG scdcExampleData = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34} }; -u8 hdmi_infoframe_poll(state_struct *state) +int hdmi_infoframe_poll(state_struct *state) { GENERAL_Read_Register_response regresp; - u32 regread; struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); + u32 regread; + u32 i; /* Unmask "AVI InfoFrame received" interrupt */ CDN_API_General_Write_Register_blocking(state, ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2), F_TMDS_MHL_HD_MASK(0xFD)); - dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt unmasked.\n"); /* Unmask "tmds_mhl_hd_int" interrupt */ CDN_API_General_Write_Register_blocking(state, ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2), F_MHL_HD_INT_MASK(0xE)); - dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt unmasked.\n"); /* MHL_HD_INT_STAT */ - dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...\n"); - do { - CDN_API_General_Read_Register_blocking(state, - ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2), - ®resp); - } while (!(regresp.val & (1 << 0))); - dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...DONE\n"); + CDN_API_General_Read_Register_blocking(state, + ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2), ®resp); + for (i = 0; i < 5; i++) { + if (regresp.val & (1 << 0)) + break; + else + CDN_API_General_Read_Register_blocking(state, + ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2), ®resp); + msleep(20); + } + if (i == 5) { + dev_err(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...failed!\n"); + return -1; + } else + dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...DONE\n"); /* Mask "AVI InfoFrame received" interrupt */ CDN_API_General_Write_Register_blocking(state, ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2), F_TMDS_MHL_HD_MASK(0xFF)); - dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt masked.\n"); /* Mask "tmds_mhl_hd_int" interrupt */ CDN_API_General_Write_Register_blocking(state, ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2), F_MHL_HD_INT_MASK(0xF)); - dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt masked.\n"); /* Read back TMDS_MHL_HD_INT_STAT to clear the interrupt */ CDN_API_General_Read_Register_blocking(state, ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_STAT << 2), ®resp); - dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt cleared.\n"); /* Packet 0 read request */ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2), F_PACKET_RDN_WR(0x0) | F_PACKET_NUM(0x0)); - dev_dbg(&hdmi_rx->pdev->dev, "PIF Packet 0 read request.\n"); /* Wait for pkt_host_rdy_status */ - dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...\n"); - do { - cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); - } while (!(regread & (1 << 16))); - dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...DONE\n"); + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); + for (i = 0; i < 5; i++) { + if (regread & (1 << 16)) + break; + else + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); + msleep(20); + } + if (i == 5) { + dev_err(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...FAILED!\n"); + return -1; + } else + dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...DONE\n"); return 0; } /* Exemplary code to configure the controller */ /* for the AVI_InfoFrame reception */ -static void get_avi_infoframe(state_struct *state) +static int get_avi_infoframe(state_struct *state) { struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); GENERAL_Read_Register_response regresp; + int ret; dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__); /* Get VIC code and pixel encoding */ - hdmi_infoframe_poll(state); + ret = hdmi_infoframe_poll(state); + if (ret < 0) + return -1; CDN_API_General_Read_Register_blocking(state, ADDR_SINK_MHL_HD + (PKT_AVI_DATA_LOW << 2), ®resp); hdmi_rx->vic_code = (regresp.val & 0xFF000000) >> 24; hdmi_rx->pixel_encoding = (regresp.val & 0x00000060) >> 5; + return 0; } -static void get_vendor_infoframe(state_struct *state) +static int get_vendor_infoframe(state_struct *state) { struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); u32 regread; + int ret; dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__); /* Set info_type1 to vendor Info Frame */ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x81)); - hdmi_infoframe_poll(state); + ret = hdmi_infoframe_poll(state); + if (ret < 0) + return -1; /* Get IEEE OUI */ cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), ®read); @@ -177,6 +195,8 @@ static void get_vendor_infoframe(state_struct *state) /* Clear info_type1 */ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x00)); + + return 0; } static void get_color_depth(struct mxc_hdmi_rx_dev *hdmi_rx, @@ -246,9 +266,32 @@ static void get_color_depth(struct mxc_hdmi_rx_dev *hdmi_rx, } } -int hdmi_rx_init(state_struct *state) +/* Set edid data sample */ +void hdmirx_edid_set(state_struct *state) { + struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); + + /* Set EDID - block 0 */ + CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 0, &block0[0]); + /* Set EDID - block 1 */ + CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 1, &block1[0]); + dev_dbg(&hdmi_rx->pdev->dev, "EDID block 0/1 set complete.\n"); +} + +/* Set SCDC data sample */ +void hdmirx_scdc_set(state_struct *state) +{ + struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); + + CDN_API_HDMIRX_SET_SCDC_SLAVE_blocking(state, &scdcExampleData); + dev_dbg(&hdmi_rx->pdev->dev, "SCDC set complete.\n"); +} + +int hdmirx_init(state_struct *state) +{ + u8 sts; u32 ret = 0; + /* Set uCPU Clock frequency for FW's use [MHz]; */ CDN_API_SetClock(state, 200); @@ -257,23 +300,63 @@ int hdmi_rx_init(state_struct *state) /* Check if the firmware is running */ ret = CDN_API_CheckAlive_blocking(state); + if (ret < 0) + return ret; + + /* Set driver and firmware active */ + CDN_API_MainControl_blocking(state, 1, &sts); + /* set sample edid and scdc */ + hdmirx_edid_set(state); + hdmirx_scdc_set(state); + return ret; } +int hdmirx_check5v(state_struct *state) +{ + struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); + u8 event5V = 0; + u32 i; + + /* check for 5v to get hdmi cable state */ + CDN_API_HDMIRX_ReadEvent(state, &event5V); + for (i = 0; i < 5; i++) { + if (event5V & (1 << HDMI_RX_EVENT_5V_VAL)) { + dev_dbg(&hdmi_rx->pdev->dev, "HDMI 5V present\n"); + break; + } + msleep(20); + CDN_API_HDMIRX_ReadEvent(state, &event5V); + } + if (i == 5) { + dev_dbg(&hdmi_rx->pdev->dev, "No HDMI 5V present!!!\n"); + return -1; + } + + return 0; +} + +void hdmirx_hotplug_trigger(state_struct *state) +{ + /* Clear HPD */ + CDN_API_HDMIRX_SetHpd_blocking(state, 0); + /* provide minimum low pulse length (100ms) */ + msleep(110); + CDN_API_HDMIRX_SetHpd_blocking(state, 1); +} + /* Bring-up sequence for the HDMI-RX */ int hdmirx_startup(state_struct *state) { u8 sts; u32 rx_clk_freq; - u8 event5V = 0; u8 data_rate_change = 0; u8 scrambling_en; clk_ratio_t clk_ratio, clk_ratio_detected; tmds_bit_clock_ratio_t tmds_bit_clock_ratio; struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state); S_HDMI_SCDC_GET_MSG *scdcData = &hdmi_rx->scdcData; - u8 ret = 0; - u32 i; + int ret = 0; /* Start from TMDS/pixel clock ratio of 1:1. * It affects only pixel clock frequency as the character/data clocks are generated based on @@ -282,55 +365,16 @@ int hdmirx_startup(state_struct *state) * of the pixel clock ratio being programmed. */ clk_ratio = CLK_RATIO_1_1; - /* Set driver and firmware active */ - CDN_API_MainControl_blocking(state, 1, &sts); - - /* Set EDID - block 0 */ - CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 0, &block0[0]); - - /* Set EDID - block 1 */ - CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 1, &block1[0]); - dev_dbg(&hdmi_rx->pdev->dev, "EDID block 0/1 set complete.\n"); - - /* Set SCDC data sample */ - CDN_API_HDMIRX_SET_SCDC_SLAVE_blocking(state, &scdcExampleData); - dev_dbg(&hdmi_rx->pdev->dev, "SCDC set complete.\n"); - /* Get TMDS_Bit_Clock_Ratio and Scrambling setting */ CDN_API_HDMIRX_GET_SCDC_SLAVE_blocking(state, scdcData); tmds_bit_clock_ratio = ((scdcData->TMDS_Config & (1 << 1)) >> 1) ? - TMDS_BIT_CLOCK_RATIO_1_40 : TMDS_BIT_CLOCK_RATIO_1_10; + TMDS_BIT_CLOCK_RATIO_1_40 : TMDS_BIT_CLOCK_RATIO_1_10; scrambling_en = scdcData->TMDS_Config & (1 << 0); + dev_dbg(&hdmi_rx->pdev->dev, "TMDS ratio: 1/%0d, Scrambling %0d).\n", tmds_bit_clock_ratio, scrambling_en); - /* Clear HPD */ - CDN_API_HDMIRX_SetHpd_blocking(state, 0); - dev_dbg(&hdmi_rx->pdev->dev, "Clear HDP\n"); - - /* check for 5v to get hdmi cable state */ - CDN_API_HDMIRX_ReadEvent(state, &event5V); - dev_dbg(&hdmi_rx->pdev->dev, "event5V = 0x%02X\n", event5V); - for (i = 0; i < 5; i++) { - if (event5V & (1 << HDMI_RX_EVENT_5V_VAL)) { - dev_info(&hdmi_rx->pdev->dev, "HDMI 5V present\n"); - break; - } - msleep(20); - CDN_API_HDMIRX_ReadEvent(state, &event5V); - dev_dbg(&hdmi_rx->pdev->dev, "event5V = 0x%02X\n", event5V); - } - if (i == 5) { - dev_info(&hdmi_rx->pdev->dev, "No HDMI 5V present!!!\n"); - return -1; - } - - /* Got 5v, set hpd */ - msleep(100); /* provide minimum low pulse length (100ms) */ - CDN_API_HDMIRX_SetHpd_blocking(state, 1); - dev_dbg(&hdmi_rx->pdev->dev, "Set HDP\n"); - /* Configure the PHY */ pma_config(state); @@ -388,13 +432,23 @@ int hdmirx_startup(state_struct *state) * 5. If mismatch found - reprogram the PHY * 6. Enable the video data path in the controller */ - get_avi_infoframe(state); - get_vendor_infoframe(state); + ret = get_avi_infoframe(state); + if (ret < 0) { + pr_err("Get AVI info frame failed\n"); + return -1; + } + ret = get_vendor_infoframe(state); + if (ret < 0) + pr_info("No Vendor info frame\n"); dev_dbg(&hdmi_rx->pdev->dev, "get_avi_infoframe() vic_code: %0d, pixel_encoding: %0d.\n", hdmi_rx->vic_code, hdmi_rx->pixel_encoding); - mxc_hdmi_frame_timing(hdmi_rx); + ret = mxc_hdmi_frame_timing(hdmi_rx); + if (ret < 0) { + pr_err("Get frame timing failed\n\n"); + return -1; + } clk_ratio_detected = clk_ratio_detect(state, rx_clk_freq, hdmi_rx->timings->timings.bt. @@ -438,3 +492,24 @@ int hdmirx_startup(state_struct *state) F_SINK_CEC_SYS_CLK_RSTN_EN(1)); return 0; } + +/* Stop for the HDMI-RX */ +void hdmirx_stop(state_struct *state) +{ + CDN_API_HDMIRX_Stop_blocking(state); +} + +void hdmirx_phy_pix_engine_reset(state_struct *state) +{ + GENERAL_Read_Register_response regresp; + + CDN_API_General_Read_Register_blocking(state, ADDR_SINK_CAR + + (SINK_MHL_HD_CAR << 2), + ®resp); + CDN_API_General_Write_Register_blocking(state, ADDR_SINK_CAR + + (SINK_MHL_HD_CAR << 2), + regresp.val & 0x3F); + CDN_API_General_Write_Register_blocking(state, ADDR_SINK_CAR + + (SINK_MHL_HD_CAR << 2), + regresp.val); +} diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c index 92acd0ee2ebd..cb2e2de07dfc 100644 --- a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c @@ -11,7 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - +#include #include "mxc-hdmi-rx.h" #include "API_AFE_ss28fdsoi_hdmirx.h" @@ -243,7 +243,7 @@ static void mxc_hdmi_clock_disable(struct mxc_hdmi_rx_dev *hdmi_rx) clk_disable_unprepare(hdmi_rx->pxl_link_clk); } -static void imx8qm_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx) +static void mxc_hdmi_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx) { u32 val; @@ -276,6 +276,15 @@ static void imx8qm_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx) /* ----------------------------------------------------------------------------- * v4l2_subdev_video_ops */ +static int mxc_hdmi_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) +{ + struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd); + struct device *dev = &hdmi_rx->pdev->dev; + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} static int mxc_hdmi_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { @@ -318,7 +327,7 @@ static int mxc_hdmi_s_stream(struct v4l2_subdev *sd, int enable) u32 val; dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__); - imx8qm_pixel_link_encoder(hdmi_rx); + mxc_hdmi_pixel_link_encoder(hdmi_rx); if (enable) { val = readl(hdmi_rx->mem.ss_base); @@ -346,6 +355,7 @@ static int mxc_hdmi_s_stream(struct v4l2_subdev *sd, int enable) static const struct v4l2_subdev_video_ops imx_video_ops_hdmi = { .s_stream = mxc_hdmi_s_stream, .g_parm = mxc_hdmi_g_parm, + .s_parm = mxc_hdmi_s_parm, }; /* ----------------------------------------------------------------------------- @@ -578,24 +588,32 @@ void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset) static int imx8qm_hdp_read(struct hdp_mem *mem, u32 addr, u32 *value) { u32 temp; - void *tmp_addr = (addr & 0xfff) + mem->regs_base; - void *off_addr = 0x4 + mem->ss_base;; + void *tmp_addr; + void *off_addr; + mutex_lock(&mem->mutex); + tmp_addr = (addr & 0xfff) + mem->regs_base; + off_addr = 0x4 + mem->ss_base; __raw_writel(addr >> 12, off_addr); temp = __raw_readl((volatile u32 *)tmp_addr); *value = temp; + mutex_unlock(&mem->mutex); return 0; } static int imx8qm_hdp_write(struct hdp_mem *mem, u32 addr, u32 value) { - void *tmp_addr = (addr & 0xfff) + mem->regs_base; - void *off_addr = 0x4 + mem->ss_base;; + void *tmp_addr; + void *off_addr; + mutex_lock(&mem->mutex); + tmp_addr = (addr & 0xfff) + mem->regs_base; + off_addr = 0x4 + mem->ss_base;; __raw_writel(addr >> 12, off_addr); __raw_writel(value, (volatile u32 *) tmp_addr); + mutex_unlock(&mem->mutex); return 0; } @@ -603,23 +621,31 @@ static int imx8qm_hdp_write(struct hdp_mem *mem, u32 addr, u32 value) static int imx8qm_hdp_sread(struct hdp_mem *mem, u32 addr, u32 *value) { u32 temp; - void *tmp_addr = (addr & 0xfff) + mem->regs_base; - void *off_addr = 0xc + mem->ss_base;; + void *tmp_addr; + void *off_addr; + mutex_lock(&mem->mutex); + tmp_addr = (addr & 0xfff) + mem->regs_base; + off_addr = 0xc + mem->ss_base; __raw_writel(addr >> 12, off_addr); temp = __raw_readl((volatile u32 *)tmp_addr); *value = temp; + mutex_unlock(&mem->mutex); return 0; } static int imx8qm_hdp_swrite(struct hdp_mem *mem, u32 addr, u32 value) { - void *tmp_addr = (addr & 0xfff) + mem->regs_base; - void *off_addr = 0xc + mem->ss_base; + void *tmp_addr; + void *off_addr; + mutex_lock(&mem->mutex); + tmp_addr = (addr & 0xfff) + mem->regs_base; + off_addr = 0xc + mem->ss_base; __raw_writel(addr >> 12, off_addr); __raw_writel(value, (volatile u32 *)tmp_addr); + mutex_unlock(&mem->mutex); return 0; } @@ -662,7 +688,6 @@ static void mxc_hdmi_cec_init(struct mxc_hdmi_rx_dev *hdmi_rx) int mxc_hdmi_init(struct mxc_hdmi_rx_dev *hdmi_rx) { sc_err_t sciErr; - u32 ret = 0; dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__); mxc_hdmi_state_init(hdmi_rx); @@ -679,9 +704,59 @@ int mxc_hdmi_init(struct mxc_hdmi_rx_dev *hdmi_rx) return -EINVAL; } - ret = hdmi_rx_init(&hdmi_rx->state); - return ret; + return 0; +} + +static void hpd5v_work_func(struct work_struct *work) +{ + struct mxc_hdmi_rx_dev *hdmi_rx = container_of(work, struct mxc_hdmi_rx_dev, + hpd5v_work.work); + char event_string[32]; + char *envp[] = { event_string, NULL }; + int hpd5v; + + /* Check cable states before enable irq */ + hpd5v = hdmirx_check5v(&hdmi_rx->state); + if (hpd5v == 0) { + pr_info("HDMI RX Cable Plug In\n"); + hdmirx_hotplug_trigger(&hdmi_rx->state); + hdmirx_startup(&hdmi_rx->state); + enable_irq(hdmi_rx->irq[HPD5V_IRQ_OUT]); + sprintf(event_string, "EVENT=hdmirxin"); + kobject_uevent_env(&hdmi_rx->pdev->dev.kobj, KOBJ_CHANGE, envp); +#ifdef CONFIG_IMX_HDP_CEC + if (hdmi_rx->is_cec) { + mxc_hdmi_cec_init(hdmi_rx); + imx_cec_register(&hdmi_rx->cec); + } +#endif + } else { + pr_info("HDMI RX Cable Plug Out\n"); + hdmirx_stop(&hdmi_rx->state); +#ifdef CONFIG_IMX_HDP_CEC + if (hdmi_rx->is_cec) { + imx_cec_unregister(&hdmi_rx->cec); + } +#endif + hdmirx_phy_pix_engine_reset(&hdmi_rx->state); + sprintf(event_string, "EVENT=hdmirxout"); + kobject_uevent_env(&hdmi_rx->pdev->dev.kobj, KOBJ_CHANGE, envp); + enable_irq(hdmi_rx->irq[HPD5V_IRQ_IN]); + } +} + +#define HOTPLUG_DEBOUNCE_MS 200 +static irqreturn_t mxc_hdp5v_irq_thread(int irq, void *data) +{ + struct mxc_hdmi_rx_dev *hdmi_rx = data; + + disable_irq_nosync(irq); + + mod_delayed_work(system_wq, &hdmi_rx->hpd5v_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); + + return IRQ_HANDLED; } static int mxc_hdmi_probe(struct platform_device *pdev) @@ -690,6 +765,7 @@ static int mxc_hdmi_probe(struct platform_device *pdev) struct mxc_hdmi_rx_dev *hdmi_rx; struct resource *res; int ret = 0; + int hpd5v; dev_dbg(dev, "%s\n", __func__); hdmi_rx = devm_kzalloc(dev, sizeof(*hdmi_rx), GFP_KERNEL); @@ -698,8 +774,7 @@ static int mxc_hdmi_probe(struct platform_device *pdev) hdmi_rx->pdev = pdev; - spin_lock_init(&hdmi_rx->slock); - + mutex_init(&hdmi_rx->mem.mutex); /* register map */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi_rx->mem.regs_base = devm_ioremap_resource(dev, res); @@ -715,21 +790,16 @@ static int mxc_hdmi_probe(struct platform_device *pdev) return -EINVAL; } -#if 0 - /* TODO B0 */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_warn(dev, "Failed to get IRQ resource\n"); - return -EINVAL; - } + hdmi_rx->irq[HPD5V_IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); + if (hdmi_rx->irq[HPD5V_IRQ_IN] < 0) + dev_info(&pdev->dev, "No plug_in irq number\n"); + + hdmi_rx->irq[HPD5V_IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); + if (hdmi_rx->irq[HPD5V_IRQ_OUT] < 0) + dev_info(&pdev->dev, "No plug_out irq number\n"); + + INIT_DELAYED_WORK(&hdmi_rx->hpd5v_work, hpd5v_work_func); - ret = devm_request_irq(dev, res->start, mxc_hdmi_irq_handler, - 0, dev_name(dev), mxc_hdmi); - if (ret < 0) { - dev_err(dev, "failed to install irq (%d)\n", ret); - return -EINVAL; - } -#endif v4l2_subdev_init(&hdmi_rx->sd, &imx_ops_hdmi); /* sd.dev may use by match_of */ @@ -775,25 +845,56 @@ static int mxc_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); ret = mxc_hdmi_init(hdmi_rx); - if (ret) { - dev_info(dev, "mxc hdmi rx init failed\n"); + if (ret < 0) { + dev_err(dev, "mxc hdmi init failed\n"); goto failed; } - ret = hdmirx_startup(&hdmi_rx->state); - if (ret) { - dev_info(dev, "mxc hdmi rx startup failed\n"); + ret = hdmirx_init(&hdmi_rx->state); + if (ret < 0) { + dev_err(dev, "mxc hdmi rx init failed\n"); goto failed; } -#ifdef CONFIG_IMX_HDP_CEC - if (hdmi_rx->is_cec) { - mxc_hdmi_cec_init(hdmi_rx); - imx_cec_register(&hdmi_rx->cec); + + /* Check cable states before enable irq */ + hpd5v = hdmirx_check5v(&hdmi_rx->state); + + /* Enable Hotplug Detect IRQ thread */ + if (hdmi_rx->irq[HPD5V_IRQ_IN] > 0) { + irq_set_status_flags(hdmi_rx->irq[HPD5V_IRQ_IN], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, hdmi_rx->irq[HPD5V_IRQ_IN], + NULL, mxc_hdp5v_irq_thread, + IRQF_ONESHOT, dev_name(dev), hdmi_rx); + if (ret) { + dev_err(&pdev->dev, "can't claim irq %d\n", + hdmi_rx->irq[HPD5V_IRQ_IN]); + goto failed; + } + /* Cable Disconnedted, enable Plug in IRQ */ + if (hpd5v < 0) + enable_irq(hdmi_rx->irq[HPD5V_IRQ_IN]); } -#endif + if (hdmi_rx->irq[HPD5V_IRQ_OUT] > 0) { + irq_set_status_flags(hdmi_rx->irq[HPD5V_IRQ_OUT], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, hdmi_rx->irq[HPD5V_IRQ_OUT], + NULL, mxc_hdp5v_irq_thread, + IRQF_ONESHOT, dev_name(dev), hdmi_rx); + if (ret) { + dev_err(&pdev->dev, "can't claim irq %d\n", + hdmi_rx->irq[HPD5V_IRQ_OUT]); + goto failed; + } + if (hpd5v == 0) { + hdmirx_hotplug_trigger(&hdmi_rx->state); + hdmirx_startup(&hdmi_rx->state); + /* Cable Connected, enable Plug out IRQ */ + enable_irq(hdmi_rx->irq[HPD5V_IRQ_OUT]); + } + } + mxc_hdmi_rx_register_audio_driver(dev); - dev_info(dev, "mxc hdmi rx probe successfully\n"); + dev_info(dev, "iMX8 HDMI RX probe successfully\n"); return ret; failed: diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h index 5c892d18161c..4ba9ba59028d 100644 --- a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h @@ -76,8 +76,13 @@ struct mxc_hdmi_rx_dev_video_standards { u8 fps; }; +enum hdp_rx_irq { + HPD5V_IRQ_IN, + HPD5V_IRQ_OUT, + HPD5V_IRQ_NUM, +}; + struct mxc_hdmi_rx_dev { - spinlock_t slock; struct mutex lock; wait_queue_head_t irq_queue; struct media_pad pads[MXC_HDMI_RX_PADS_NUM]; @@ -122,6 +127,9 @@ struct mxc_hdmi_rx_dev { u32 sample_rate; u32 sample_width; u32 channels; + + int irq[HPD5V_IRQ_NUM]; + struct delayed_work hpd5v_work; }; enum mxc_hdmi_rx_power_state { @@ -130,10 +138,14 @@ enum mxc_hdmi_rx_power_state { MXC_HDMI_RX_RUNTIME_SUSPEND = 0x04, }; +void hdmirx_stop(state_struct *state); +void hdmirx_hotplug_trigger(state_struct *state); +void hdmirx_phy_pix_engine_reset(state_struct *state); int hdmirx_startup(state_struct *state); -void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset); -int hdmi_rx_init(state_struct *state); +int hdmirx_init(state_struct *state); +int hdmirx_check5v(state_struct *state); int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx); void mxc_hdmi_rx_register_audio_driver(struct device *dev); +void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset); #endif -- 2.17.1