From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 06 Nov 2023 03:04:41 +0000 Subject: [PATCH] rk3568 rt init --- kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 437 insertions(+), 83 deletions(-) diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index ee7f83a..7ba60fb 100644 --- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -49,6 +49,7 @@ #define DDC_SEGMENT_ADDR 0x30 #define HDMI_EDID_LEN 512 +#define HDMI_EDID_BLOCK_LEN 128 /* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */ #define SCDC_MIN_SOURCE_VERSION 0x1 @@ -320,6 +321,7 @@ struct drm_display_mode previous_mode; struct i2c_adapter *ddc; + struct cec_adapter *cec_adap; void __iomem *regs; bool sink_is_hdmi; bool sink_has_audio; @@ -358,10 +360,13 @@ struct cec_notifier *cec_notifier; bool initialized; /* hdmi is enabled before bind */ + bool logo_plug_out; /* hdmi is plug out when kernel logo */ hdmi_codec_plugged_cb plugged_cb; struct device *codec_dev; enum drm_connector_status last_connector_result; bool rgb_quant_range_selectable; + bool update; + bool hdr2sdr; /* from hdr to sdr */ }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -456,10 +461,11 @@ change = drm_helper_hpd_irq_event(hdmi->bridge.dev); #ifdef CONFIG_CEC_NOTIFIER - if (change) + if (change) { cec_notifier_repo_cec_hpd(hdmi->cec_notifier, hdmi->hpd_state, ktime_get()); + } #endif } } @@ -568,7 +574,8 @@ unsigned char *buf, unsigned int length) { struct dw_hdmi_i2c *i2c = hdmi->i2c; - int stat; + int stat, retry, i; + bool read_edid = false; if (!i2c->is_regaddr) { dev_dbg(hdmi->dev, "set read register address to 0\n"); @@ -576,27 +583,81 @@ i2c->is_regaddr = true; } - while (length--) { - reinit_completion(&i2c->cmp); + /* edid reads are in 128 bytes. scdc reads are in 1 byte */ + if (length == HDMI_EDID_BLOCK_LEN) + read_edid = true; - hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); - if (i2c->is_segment) - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, - HDMI_I2CM_OPERATION); - else - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, - HDMI_I2CM_OPERATION); + while (length > 0) { + retry = 100; + hdmi_writeb(hdmi, i2c->slave_reg, HDMI_I2CM_ADDRESS); - stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); - if (!stat) - return -EAGAIN; + if (read_edid) { + i2c->slave_reg += 8; + length -= 8; + } else { + i2c->slave_reg++; + length--; + } - /* Check for error condition on the bus */ - if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) + while (retry > 0) { + if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)) { + void *data = hdmi->plat_data->phy_data; + + dev_dbg(hdmi->dev, "hdmi disconnect, stop ddc read\n"); + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(data, false); + return -EPERM; + } + reinit_completion(&i2c->cmp); + if (i2c->is_segment) { + if (read_edid) + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8_EXT, + HDMI_I2CM_OPERATION); + else + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, + HDMI_I2CM_OPERATION); + } else { + if (read_edid) + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8, + HDMI_I2CM_OPERATION); + else + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, + HDMI_I2CM_OPERATION); + } + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) { + dev_dbg(hdmi->dev, "ddc read time out\n"); + hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, + HDMI_I2CM_OPERATION); + retry -= 10; + continue; + } + /* Check for error condition on the bus */ + if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) { + dev_dbg(hdmi->dev, "ddc read err\n"); + hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, + HDMI_I2CM_OPERATION); + retry--; + usleep_range(10000, 11000); + continue; + } + /* read success */ + break; + } + if (retry <= 0) { + dev_err(hdmi->dev, "ddc read failed\n"); return -EIO; + } - *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); + if (read_edid) + for (i = 0; i < 8; i++) + *buf++ = hdmi_readb(hdmi, HDMI_I2CM_READ_BUFF0 + i); + else + *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); } + i2c->is_segment = false; return 0; @@ -606,7 +667,7 @@ unsigned char *buf, unsigned int length) { struct dw_hdmi_i2c *i2c = hdmi->i2c; - int stat; + int stat, retry; if (!i2c->is_regaddr) { /* Use the first write byte as register address */ @@ -617,20 +678,53 @@ } while (length--) { - reinit_completion(&i2c->cmp); + retry = 100; hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO); hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE, - HDMI_I2CM_OPERATION); - stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); - if (!stat) - return -EAGAIN; + while (retry > 0) { + if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)) { + void *data = hdmi->plat_data->phy_data; - /* Check for error condition on the bus */ - if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) + dev_dbg(hdmi->dev, "hdmi disconnect, stop ddc write\n"); + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(data, false); + return -EPERM; + } + reinit_completion(&i2c->cmp); + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE, + HDMI_I2CM_OPERATION); + + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) { + dev_dbg(hdmi->dev, "ddc write time out\n"); + hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, + HDMI_I2CM_OPERATION); + retry -= 10; + continue; + } + + /* Check for error condition on the bus */ + if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) { + dev_dbg(hdmi->dev, "ddc write err\n"); + hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR, + HDMI_I2CM_OPERATION); + retry--; + usleep_range(10000, 11000); + continue; + } + + /* write success */ + break; + } + + if (retry <= 0) { + dev_err(hdmi->dev, "ddc write failed\n"); return -EIO; + } } return 0; @@ -642,6 +736,7 @@ struct dw_hdmi *hdmi = i2c_get_adapdata(adap); struct dw_hdmi_i2c *i2c = hdmi->i2c; u8 addr = msgs[0].addr; + void *data = hdmi->plat_data->phy_data; int i, ret = 0; if (addr == DDC_CI_ADDR) @@ -665,6 +760,12 @@ } mutex_lock(&i2c->lock); + + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(data, true); + + hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ); + udelay(100); /* Unmute DONE and ERROR interrupts */ hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0); @@ -1378,14 +1479,7 @@ HDMI_VP_CONF_PR_EN_MASK | HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); - if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) || - (color_depth == 6 && hdmi->previous_mode.htotal % 2)) - hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, - HDMI_VP_STUFF); - else - hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, - HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); - + hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { @@ -1943,10 +2037,22 @@ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; break; } + frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED; } else { - frame.colorimetry = HDMI_COLORIMETRY_NONE; - frame.extended_colorimetry = - HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; + if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { + frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; + frame.extended_colorimetry = + HDMI_EXTENDED_COLORIMETRY_BT2020; + } else { + frame.colorimetry = HDMI_COLORIMETRY_NONE; + frame.extended_colorimetry = + HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; + } + + if (is_hdmi2 && frame.quantization_range == HDMI_QUANTIZATION_RANGE_FULL) + frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_FULL; + else + frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED; } frame.scan_mode = HDMI_SCAN_MODE_NONE; @@ -1986,7 +2092,11 @@ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); /* AVI data byte 4 differences: none */ - val = frame.video_code & 0x7f; + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || + hdmi->connector.display_info.hdmi.scdc.supported) + val = hdmi->vic; + else + val = frame.video_code & 0x7f; hdmi_writeb(hdmi, val, HDMI_FC_AVIVID); /* AVI Data Byte 5- set up input and output pixel repetition */ @@ -2023,6 +2133,14 @@ struct hdmi_vendor_infoframe frame; u8 buffer[10]; ssize_t err; + + /* if sink support hdmi2.0, don't send vsi */ + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || + hdmi->connector.display_info.hdmi.scdc.supported) { + hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); + return; + } err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, &hdmi->connector, @@ -2188,6 +2306,11 @@ hdmi_writeb(hdmi, HDR_LSB(frame.max_fall), HDMI_FC_DRM_PB24); hdmi_writeb(hdmi, HDR_MSB(frame.max_fall), HDMI_FC_DRM_PB25); hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP); + /* + * avi and hdr infoframe cannot be sent at the same time + * for compatibility with Huawei TV + */ + msleep(300); hdmi_modb(hdmi, HDMI_FC_PACKET_DRM_TX_EN, HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN); @@ -2215,6 +2338,9 @@ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) vmode->mtmdsclock /= 2; + + if (hdmi->update) + return; /* Set up HDMI_FC_INVIDCONF * Some display equipments require that the interval @@ -2373,11 +2499,13 @@ hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); /* Enable pixel clock and tmds data path */ - hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | - HDMI_MC_CLKDIS_CSCCLK_DISABLE | - HDMI_MC_CLKDIS_AUDCLK_DISABLE | - HDMI_MC_CLKDIS_PREPCLK_DISABLE | - HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + + if (!hdmi->update) + hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | + HDMI_MC_CLKDIS_CSCCLK_DISABLE | + HDMI_MC_CLKDIS_AUDCLK_DISABLE | + HDMI_MC_CLKDIS_PREPCLK_DISABLE | + HDMI_MC_CLKDIS_TMDSCLK_DISABLE; hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); @@ -2455,11 +2583,42 @@ HDMI_IH_MUTE_FC_STAT2); } +static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, struct drm_display_mode *mode) +{ + /* force output black */ + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + enum hdmi_quantization_range rgb_quant_range = drm_default_rgb_quant_range(mode); + + if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_FULL) { + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS2); /*R*/ + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS1); /*G*/ + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS0); /*B*/ + } else if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) { + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS2); /*R*/ + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*G*/ + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS0); /*B*/ + } else if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_DEFAULT) { + if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_FULL) { + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS2); /*R*/ + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS1); /*G*/ + hdmi_writeb(hdmi, 0x00, HDMI_FC_DBGTMDS0); /*B*/ + } else if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) { + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS2); /*R*/ + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*G*/ + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS0); /*B*/ + } + } + } else { + hdmi_writeb(hdmi, 0x80, HDMI_FC_DBGTMDS2); /*Cr*/ + hdmi_writeb(hdmi, 0x10, HDMI_FC_DBGTMDS1); /*Y*/ + hdmi_writeb(hdmi, 0x80, HDMI_FC_DBGTMDS0); /*Cb*/ + } +} + static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) { int ret; void *data = hdmi->plat_data->phy_data; - bool need_delay = false; hdmi_disable_overflow_interrupts(hdmi); @@ -2508,6 +2667,9 @@ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; + if (hdmi->plat_data->set_prev_bus_format) + hdmi->plat_data->set_prev_bus_format(data, hdmi->hdmi_data.enc_out_bus_format); + /* TOFIX: Get input encoding from plat data or fallback to none */ if (hdmi->plat_data->get_enc_in_encoding) hdmi->hdmi_data.enc_in_encoding = @@ -2537,23 +2699,11 @@ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0; hdmi->hdmi_data.video_mode.mdataenablepolarity = true; + dw_hdmi_force_output_pattern(hdmi, mode); + /* HDMI Initialization Step B.1 */ hdmi_av_composer(hdmi, mode); - /* HDMI Initializateion Step B.2 */ - if (!hdmi->phy.enabled || - hdmi->hdmi_data.video_mode.previous_pixelclock != - hdmi->hdmi_data.video_mode.mpixelclock || - hdmi->hdmi_data.video_mode.previous_tmdsclock != - hdmi->hdmi_data.video_mode.mtmdsclock) { - ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, - &hdmi->previous_mode); - if (ret) - return ret; - hdmi->phy.enabled = true; - } else { - need_delay = true; - } /* HDMI Initialization Step B.3 */ dw_hdmi_enable_video_path(hdmi); @@ -2582,11 +2732,31 @@ hdmi_video_sample(hdmi); hdmi_tx_hdcp_config(hdmi, mode); + /* HDMI Enable phy output */ + if (!hdmi->phy.enabled || + hdmi->hdmi_data.video_mode.previous_pixelclock != + hdmi->hdmi_data.video_mode.mpixelclock || + hdmi->hdmi_data.video_mode.previous_tmdsclock != + hdmi->hdmi_data.video_mode.mtmdsclock) { + ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, + &hdmi->previous_mode); + if (ret) + return ret; + hdmi->phy.enabled = true; + } + dw_hdmi_clear_overflow(hdmi); - /* XXX: Add delay to make csc work before unmute video. */ - if (need_delay) + /* + * konka tv should switch pattern after set to yuv420 10bit or + * the TV might not recognize the signal. + */ + if (!hdmi->update) { + hdmi_writeb(hdmi, 1, HDMI_FC_DBGFORCE); msleep(50); + hdmi_writeb(hdmi, 0, HDMI_FC_DBGFORCE); + } + return 0; } @@ -2686,6 +2856,7 @@ if (hdmi->initialized) { hdmi->initialized = false; hdmi->disabled = true; + hdmi->logo_plug_out = true; } if (hdmi->bridge_is_on) dw_hdmi_poweroff(hdmi); @@ -2793,6 +2964,8 @@ edid = drm_get_edid(connector, hdmi->ddc); if (edid) { + int vic = 0; + dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", edid->width_cm, edid->height_cm); @@ -2802,7 +2975,18 @@ drm_connector_update_edid_property(connector, edid); cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); ret = drm_add_edid_modes(connector, edid); - dw_hdmi_update_hdr_property(connector); + + list_for_each_entry(mode, &connector->probed_modes, head) { + vic = drm_match_cea_mode(mode); + + if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_NONE) { + if (vic >= 93 && vic <= 95) + mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; + else if (vic == 98) + mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; + } + } + kfree(edid); } else { hdmi->support_hdmi = true; @@ -2815,11 +2999,8 @@ mode = drm_mode_duplicate(connector->dev, ptr); if (mode) { - if (!i) { + if (!i) mode->type = DRM_MODE_TYPE_PREFERRED; - mode->picture_aspect_ratio = - HDMI_PICTURE_ASPECT_NONE; - } drm_mode_probed_add(connector, mode); ret++; } @@ -2830,6 +3011,7 @@ dev_info(hdmi->dev, "failed to get edid\n"); } + dw_hdmi_update_hdr_property(connector); dw_hdmi_check_output_type_changed(hdmi); return ret; @@ -2892,19 +3074,86 @@ return ret; } -static bool hdr_metadata_equal(const struct drm_connector_state *old_state, +static bool hdr_metadata_equal(struct dw_hdmi *hdmi, const struct drm_connector_state *old_state, const struct drm_connector_state *new_state) { struct drm_property_blob *old_blob = old_state->hdr_output_metadata; struct drm_property_blob *new_blob = new_state->hdr_output_metadata; + int i, ret; + u8 *data; - if (!old_blob || !new_blob) - return old_blob == new_blob; + hdmi->hdr2sdr = false; + + if (!old_blob && !new_blob) + return true; + + if (!old_blob) { + data = (u8 *)new_blob->data; + + for (i = 0; i < new_blob->length; i++) + if (data[i]) + return false; + + return true; + } + + if (!new_blob) { + data = (u8 *)old_blob->data; + + for (i = 0; i < old_blob->length; i++) + if (data[i]) + return false; + + return true; + } if (old_blob->length != new_blob->length) return false; - return !memcmp(old_blob->data, new_blob->data, old_blob->length); + ret = !memcmp(old_blob->data, new_blob->data, old_blob->length); + + if (!ret && new_blob) { + data = (u8 *)new_blob->data; + + for (i = 0; i < new_blob->length; i++) + if (data[i]) + break; + + if (i == new_blob->length) + hdmi->hdr2sdr = true; + } + + return ret; +} + +static bool check_hdr_color_change(struct drm_connector_state *old_state, + struct drm_connector_state *new_state, + struct dw_hdmi *hdmi) +{ + void *data = hdmi->plat_data->phy_data; + + if (!hdr_metadata_equal(hdmi, old_state, new_state)) { + hdmi->plat_data->check_hdr_color_change(new_state, data); + return true; + } + + return false; +} + +static bool check_hdmi_format_change(struct drm_connector_state *old_state, + struct drm_connector_state *new_state, + struct drm_connector *connector, + struct dw_hdmi *hdmi) +{ + bool hdr_change, color_change; + + hdr_change = check_hdr_color_change(old_state, new_state, hdmi); + color_change = dw_hdmi_color_changed(connector); + + if (hdr_change || color_change) + return true; + + return false; } static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, @@ -2925,12 +3174,19 @@ if (!crtc) return 0; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + mode = &crtc_state->mode; + /* * If HDMI is enabled in uboot, it's need to record * drm_display_mode and set phy status to enabled. */ if (!vmode->mpixelclock) { - crtc_state = drm_atomic_get_crtc_state(state, crtc); + u8 val; + if (hdmi->plat_data->get_enc_in_encoding) hdmi->hdmi_data.enc_in_encoding = hdmi->plat_data->get_enc_in_encoding(data); @@ -2944,27 +3200,84 @@ hdmi->hdmi_data.enc_out_bus_format = hdmi->plat_data->get_output_bus_format(data); - mode = &crtc_state->mode; memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); vmode->mpixelclock = mode->crtc_clock * 1000; - vmode->previous_pixelclock = mode->clock; - vmode->previous_tmdsclock = mode->clock; + vmode->previous_pixelclock = mode->clock * 1000; + vmode->previous_tmdsclock = mode->clock * 1000; vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) vmode->mtmdsclock /= 2; + + dw_hdmi_force_output_pattern(hdmi, mode); + + hdmi_clk_regenerator_update_pixel_clock(hdmi); + hdmi_enable_audio_clk(hdmi, hdmi->audio_enable); + + drm_scdc_readb(hdmi->ddc, SCDC_TMDS_CONFIG, &val); + + /* if plug out before hdmi bind, reset hdmi */ + if (vmode->mtmdsclock >= 340000000 && !(val & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40)) + hdmi->logo_plug_out = true; } - if (!hdr_metadata_equal(old_state, new_state) || - dw_hdmi_color_changed(connector)) { - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); + if (check_hdmi_format_change(old_state, new_state, connector, hdmi) || + hdmi->logo_plug_out) { + u32 mtmdsclk; - crtc_state->mode_changed = true; + if (hdmi->plat_data->update_color_format) + hdmi->plat_data->update_color_format(new_state, data); + if (hdmi->plat_data->get_enc_in_encoding) + hdmi->hdmi_data.enc_in_encoding = + hdmi->plat_data->get_enc_in_encoding(data); + if (hdmi->plat_data->get_enc_out_encoding) + hdmi->hdmi_data.enc_out_encoding = + hdmi->plat_data->get_enc_out_encoding(data); + if (hdmi->plat_data->get_input_bus_format) + hdmi->hdmi_data.enc_in_bus_format = + hdmi->plat_data->get_input_bus_format(data); + if (hdmi->plat_data->get_output_bus_format) + hdmi->hdmi_data.enc_out_bus_format = + hdmi->plat_data->get_output_bus_format(data); + + mtmdsclk = hdmi_get_tmdsclock(hdmi, mode->clock); + + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) + mtmdsclk /= 2; + + if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)) + return 0; + + if (hdmi->hdmi_data.video_mode.mpixelclock == (mode->clock * 1000) && + hdmi->hdmi_data.video_mode.mtmdsclock == (mtmdsclk * 1000) && + !hdmi->logo_plug_out && !hdmi->disabled) { + hdmi->update = true; + hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); + mdelay(180); + handle_plugged_change(hdmi, false); + } else { + hdmi->update = false; + crtc_state->mode_changed = true; + hdmi->logo_plug_out = false; + } } return 0; +} + +static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *state) +{ + struct dw_hdmi *hdmi = + container_of(connector, struct dw_hdmi, connector); + + if (hdmi->update) { + dw_hdmi_setup(hdmi, &hdmi->previous_mode); + mdelay(50); + handle_plugged_change(hdmi, true); + hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); + hdmi->update = false; + } } void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) @@ -3006,6 +3319,15 @@ } EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap); +void dw_hdmi_set_hpd_wake(struct dw_hdmi *hdmi) +{ + if (!hdmi->cec) + return; + + dw_hdmi_hpd_wake_up(hdmi->cec); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_hpd_wake); + static void dw_hdmi_connector_force(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, @@ -3045,6 +3367,7 @@ .get_modes = dw_hdmi_connector_get_modes, .best_encoder = drm_atomic_helper_best_encoder, .atomic_check = dw_hdmi_connector_atomic_check, + .atomic_commit = dw_hdmi_connector_atomic_commit, }; static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi) @@ -3172,6 +3495,9 @@ if (hdmi->next_bridge) return MODE_OK; + if (!(hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD) && hdmi->hdr2sdr) + return MODE_OK; + if (hdmi->plat_data->mode_valid) mode_status = hdmi->plat_data->mode_valid(connector, mode); @@ -3195,12 +3521,21 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) { struct dw_hdmi *hdmi = bridge->driver_private; + void *data = hdmi->plat_data->phy_data; mutex_lock(&hdmi->mutex); hdmi->disabled = true; + handle_plugged_change(hdmi, false); dw_hdmi_update_power(hdmi); dw_hdmi_update_phy_mask(hdmi); + if (hdmi->plat_data->dclk_set) + hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, false, 0); mutex_unlock(&hdmi->mutex); + + mutex_lock(&hdmi->i2c->lock); + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(data, false); + mutex_unlock(&hdmi->i2c->lock); } static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) @@ -3209,8 +3544,11 @@ mutex_lock(&hdmi->mutex); hdmi->disabled = false; + if (hdmi->plat_data->dclk_set) + hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0); dw_hdmi_update_power(hdmi); dw_hdmi_update_phy_mask(hdmi); + handle_plugged_change(hdmi, true); mutex_unlock(&hdmi->mutex); } @@ -3456,6 +3794,7 @@ static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = { .write = hdmi_writeb, .read = hdmi_readb, + .mod = hdmi_modb, .enable = dw_hdmi_cec_enable, .disable = dw_hdmi_cec_disable, }; @@ -3464,14 +3803,14 @@ .reg_bits = 32, .val_bits = 8, .reg_stride = 1, - .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR, + .max_register = HDMI_I2CM_SCDC_UPDATE1, }; static const struct regmap_config hdmi_regmap_32bit_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR << 2, + .max_register = HDMI_I2CM_SCDC_UPDATE1 << 2, }; static int dw_hdmi_status_show(struct seq_file *s, void *v) @@ -3852,6 +4191,7 @@ hdmi->connector.stereo_allowed = 1; hdmi->plat_data = plat_data; hdmi->dev = dev; + hdmi->audio_enable = true; hdmi->sample_rate = 48000; hdmi->disabled = true; hdmi->rxsense = true; @@ -3983,6 +4323,7 @@ if (ret) goto err_iahb; + hdmi->logo_plug_out = false; hdmi->initialized = false; ret = hdmi_readb(hdmi, HDMI_PHY_STAT0); if (((ret & HDMI_PHY_TX_PHY_LOCK) && (ret & HDMI_PHY_HPD) && @@ -3992,8 +4333,14 @@ hdmi->bridge_is_on = true; hdmi->phy.enabled = true; hdmi->initialized = true; + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(hdmi->plat_data->phy_data, true); + if (hdmi->plat_data->dclk_set) + hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0); } else if (ret & HDMI_PHY_TX_PHY_LOCK) { hdmi->phy.ops->disable(hdmi, hdmi->phy.data); + if (hdmi->plat_data->set_ddc_io) + hdmi->plat_data->set_ddc_io(hdmi->plat_data->phy_data, false); } init_hpd_work(hdmi); @@ -4007,7 +4354,7 @@ hdmi->irq = irq; ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, - dw_hdmi_irq, IRQF_SHARED, + dw_hdmi_irq, IRQF_SHARED | IRQF_ONESHOT, dev_name(dev), hdmi); if (ret) goto err_iahb; @@ -4126,6 +4473,12 @@ cec.ops = &dw_hdmi_cec_ops; cec.irq = irq; + irq = platform_get_irq(pdev, 1); + if (irq < 0) + dev_dbg(hdmi->dev, "can't get cec wake up irq\n"); + + cec.wake_irq = irq; + pdevinfo.name = "dw-hdmi-cec"; pdevinfo.data = &cec; pdevinfo.size_data = sizeof(cec); @@ -4136,7 +4489,8 @@ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable); if (IS_ERR(hdmi->extcon)) { - dev_err(hdmi->dev, "allocate extcon failed\n"); + ret = PTR_ERR(hdmi->extcon); + dev_err(hdmi->dev, "allocate extcon failed: %d\n", ret); goto err_iahb; } -- Gitblit v1.6.2