aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2015-08-27 13:01:57 +1000
committerDave Airlie <airlied@redhat.com>2015-08-27 13:01:57 +1000
commitd7b273685fedba5359a4ba0ae4f542e3ece28153 (patch)
tree6d4ad284a40820492b73a0790e760ca34644e0da
parent31607793ee81da3f1024df9560f0dec49abde37f (diff)
parent6dc2e1bf8e0025db2ff8a35ee3e0bd88203d4402 (diff)
Merge branch 'drm-dwhdmi-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into drm-next
Here are some development updates for the Synopsis Designware HDMI driver, which clean up some of the code, and start preparing to add audio support to the driver. This series of patches are based on a couple of dependent commits from the ALSA tree. Briefly, the updates are: - move comments which should have moved with the phy values to the IMX part of the driver. - clean up the phy configuration: to all lookups before starting to program the phy. - clean up the HDMI clock regenerator code - use the drm_hdmi_avi_infoframe_from_display_mode() helper which allows the code to be subsequently simplified - remove the unused 'regmap' pointer in struct dw_hdmi - use the bridge drm device rather than the connector (we're the bridge code) - remove private hsync/vsync/interlaced flags, getting them from the DRM mode structure instead. - implement interface functions to support audio - setting the audio sample rate, and enabling the audio clocks. - removal of broken pixel repetition support - cleanup DVI vs HDMI sink handling - enable audio only if connected device supports audio - avoid double-enabling bridge in the sink path (once in mode_set, and again in commit) - rename mis-named dw_hdmi_phy_enable_power() - fix bridge enable/disable handing, so a plug-in event doesn't reconfigure the bridge if DRM has disabled the output - fix from Vladimir Zapolskiy for the I2CM_ADDRESS macro name These are primerily preparitory patches for the AHB audio driver and the I2S audio driver (from Rockchip) for this IP. * 'drm-dwhdmi-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm: drm: bridge/dw_hdmi: fix register I2CM_ADDRESS register name drm: bridge/dw_hdmi: fix phy enable/disable handling drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power() drm: bridge/dw_hdmi: avoid enabling interface in mode_set drm: bridge/dw_hdmi: enable audio only if sink supports audio drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling drm: bridge/dw_hdmi: don't support any pixel doubled modes drm: bridge/dw_hdmi: remove pixel repetition setting for all VICs drm: bridge/dw_hdmi: introduce interfaces to enable and disable audio drm: bridge/dw_hdmi: introduce interface to setting sample rate drm: bridge/dw_hdmi: remove mhsyncpolarity/mvsyncpolarity/minterlaced drm: bridge/dw_hdmi: use our own drm_device drm: bridge/dw_hdmi: remove unused 'regmap' struct member drm: bridge/dw_hdmi: simplify hdmi_config_AVI() a little drm: bridge/dw_hdmi: use drm_hdmi_avi_infoframe_from_display_mode() drm: bridge/dw_hdmi: clean up hdmi_set_clk_regenerator() drm: bridge/dw_hdmi: clean up phy configuration drm: imx/dw_hdmi: move phy comments drm/edid: add function to help find SADs
-rw-r--r--drivers/gpu/drm/bridge/dw_hdmi.c387
-rw-r--r--drivers/gpu/drm/bridge/dw_hdmi.h8
-rw-r--r--drivers/gpu/drm/imx/dw_hdmi-imx.c5
-rw-r--r--include/drm/bridge/dw_hdmi.h7
4 files changed, 233 insertions, 174 deletions
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 816d104ca4da..0083d4e7e7e2 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -18,6 +18,7 @@
#include <linux/hdmi.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
+#include <linux/spinlock.h>
#include <drm/drm_of.h>
#include <drm/drmP.h>
@@ -81,10 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
};
struct hdmi_vmode {
- bool mdvi;
- bool mhsyncpolarity;
- bool mvsyncpolarity;
- bool minterlaced;
bool mdataenablepolarity;
unsigned int mpixelclock;
@@ -123,12 +120,20 @@ struct dw_hdmi {
bool phy_enabled;
struct drm_display_mode previous_mode;
- struct regmap *regmap;
struct i2c_adapter *ddc;
void __iomem *regs;
+ bool sink_is_hdmi;
+ bool sink_has_audio;
+ struct mutex mutex; /* for state below and previous_mode */
+ bool disabled; /* DRM has disabled our bridge */
+
+ spinlock_t audio_lock;
struct mutex audio_mutex;
unsigned int sample_rate;
+ unsigned int audio_cts;
+ unsigned int audio_n;
+ bool audio_enable;
int ratio;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
@@ -335,42 +340,76 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
}
static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
- unsigned long pixel_clk)
+ unsigned long pixel_clk, unsigned int sample_rate, unsigned int ratio)
{
- unsigned int clk_n, clk_cts;
-
- clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk,
- hdmi->ratio);
- clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk,
- hdmi->ratio);
+ unsigned int n, cts;
- if (!clk_cts) {
- dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
- __func__, pixel_clk);
- return;
+ n = hdmi_compute_n(sample_rate, pixel_clk, ratio);
+ cts = hdmi_compute_cts(sample_rate, pixel_clk, ratio);
+ if (!cts) {
+ dev_err(hdmi->dev,
+ "%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
+ __func__, pixel_clk, sample_rate);
}
- dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n",
- __func__, hdmi->sample_rate, hdmi->ratio,
- pixel_clk, clk_n, clk_cts);
+ dev_dbg(hdmi->dev, "%s: samplerate=%ukHz ratio=%d pixelclk=%luMHz N=%d cts=%d\n",
+ __func__, sample_rate, ratio, pixel_clk, n, cts);
- hdmi_set_cts_n(hdmi, clk_cts, clk_n);
+ spin_lock_irq(&hdmi->audio_lock);
+ hdmi->audio_n = n;
+ hdmi->audio_cts = cts;
+ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
+ spin_unlock_irq(&hdmi->audio_lock);
}
static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
{
mutex_lock(&hdmi->audio_mutex);
- hdmi_set_clk_regenerator(hdmi, 74250000);
+ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate,
+ hdmi->ratio);
mutex_unlock(&hdmi->audio_mutex);
}
static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
{
mutex_lock(&hdmi->audio_mutex);
- hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
+ hdmi->sample_rate, hdmi->ratio);
mutex_unlock(&hdmi->audio_mutex);
}
+void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi->sample_rate = rate;
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
+ hdmi->sample_rate, hdmi->ratio);
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdmi->audio_lock, flags);
+ hdmi->audio_enable = true;
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+ spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
+
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdmi->audio_lock, flags);
+ hdmi->audio_enable = false;
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+ spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
+
/*
* this submodule is responsible for the video data synchronization.
* for example, for RGB 4:4:4 input, the data map is defined as
@@ -701,9 +740,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
return 0;
}
-static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
{
- hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+ hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
HDMI_PHY_CONF0_PDZ_OFFSET,
HDMI_PHY_CONF0_PDZ_MASK);
}
@@ -753,12 +792,12 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
unsigned char res, int cscon)
{
- unsigned res_idx, i;
+ unsigned res_idx;
u8 val, msec;
- const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
- const struct dw_hdmi_mpll_config *mpll_config = plat_data->mpll_cfg;
- const struct dw_hdmi_curr_ctrl *curr_ctrl = plat_data->cur_ctr;
- const struct dw_hdmi_phy_config *phy_config = plat_data->phy_config;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+ const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
+ const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
+ const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
if (prep)
return -EINVAL;
@@ -778,6 +817,30 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
return -EINVAL;
}
+ /* PLL/MPLL Cfg - always match on final entry */
+ for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
+ if (hdmi->hdmi_data.video_mode.mpixelclock <=
+ mpll_config->mpixelclock)
+ break;
+
+ for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
+ if (hdmi->hdmi_data.video_mode.mpixelclock <=
+ curr_ctrl->mpixelclock)
+ break;
+
+ for (; phy_config->mpixelclock != ~0UL; phy_config++)
+ if (hdmi->hdmi_data.video_mode.mpixelclock <=
+ phy_config->mpixelclock)
+ break;
+
+ if (mpll_config->mpixelclock == ~0UL ||
+ curr_ctrl->mpixelclock == ~0UL ||
+ phy_config->mpixelclock == ~0UL) {
+ dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
+ hdmi->hdmi_data.video_mode.mpixelclock);
+ return -EINVAL;
+ }
+
/* Enable csc path */
if (cscon)
val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
@@ -803,48 +866,23 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
HDMI_PHY_I2CM_SLAVE_ADDR);
hdmi_phy_test_clear(hdmi, 0);
- /* PLL/MPLL Cfg - always match on final entry */
- for (i = 0; mpll_config[i].mpixelclock != (~0UL); i++)
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
- mpll_config[i].mpixelclock)
- break;
-
- hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
- hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
-
- for (i = 0; curr_ctrl[i].mpixelclock != (~0UL); i++)
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
- curr_ctrl[i].mpixelclock)
- break;
-
- if (curr_ctrl[i].mpixelclock == (~0UL)) {
- dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
- hdmi->hdmi_data.video_mode.mpixelclock);
- return -EINVAL;
- }
+ hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
+ hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
/* CURRCTRL */
- hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+ hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
- for (i = 0; phy_config[i].mpixelclock != (~0UL); i++)
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
- phy_config[i].mpixelclock)
- break;
-
- /* RESISTANCE TERM 133Ohm Cfg */
- hdmi_phy_i2c_write(hdmi, phy_config[i].term, 0x19); /* TXTERM */
- /* PREEMP Cgf 0.00 */
- hdmi_phy_i2c_write(hdmi, phy_config[i].sym_ctr, 0x09); /* CKSYMTXCTRL */
- /* TX/CK LVL 10 */
- hdmi_phy_i2c_write(hdmi, phy_config[i].vlev_ctr, 0x0E); /* VLEVCTRL */
+ hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */
+ hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
+ hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
/* REMOVE CLK TERM */
hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
- dw_hdmi_phy_enable_power(hdmi, 1);
+ dw_hdmi_phy_enable_powerdown(hdmi, false);
/* toggle TMDS enable */
dw_hdmi_phy_enable_tmds(hdmi, 0);
@@ -879,18 +917,17 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
{
int i, ret;
- bool cscon = false;
+ bool cscon;
/*check csc whether needed activated in HDMI mode */
- cscon = (is_color_space_conversion(hdmi) &&
- !hdmi->hdmi_data.video_mode.mdvi);
+ cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
/* HDMI Phy spec says to do the phy initialization sequence twice */
for (i = 0; i < 2; i++) {
dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
dw_hdmi_phy_sel_interface_control(hdmi, 0);
dw_hdmi_phy_enable_tmds(hdmi, 0);
- dw_hdmi_phy_enable_power(hdmi, 0);
+ dw_hdmi_phy_enable_powerdown(hdmi, true);
/* Enable CSC */
ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
@@ -921,74 +958,76 @@ static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
}
-static void hdmi_config_AVI(struct dw_hdmi *hdmi)
+static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
{
- u8 val, pix_fmt, under_scan;
- u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry;
- bool aspect_16_9;
+ struct hdmi_avi_infoframe frame;
+ u8 val;
- aspect_16_9 = false; /* FIXME */
+ /* Initialise info frame from DRM mode */
+ drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
- /* AVI Data Byte 1 */
if (hdmi->hdmi_data.enc_out_format == YCBCR444)
- pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR444;
+ frame.colorspace = HDMI_COLORSPACE_YUV444;
else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS)
- pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR422;
+ frame.colorspace = HDMI_COLORSPACE_YUV422;
else
- pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB;
-
- under_scan = HDMI_FC_AVICONF0_SCAN_INFO_NODATA;
-
- /*
- * Active format identification data is present in the AVI InfoFrame.
- * Under scan info, no bar data
- */
- val = pix_fmt | under_scan |
- HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
- HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
-
- hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
-
- /* AVI Data Byte 2 -Set the Aspect Ratio */
- if (aspect_16_9) {
- act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9;
- coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9;
- } else {
- act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3;
- coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3;
- }
+ frame.colorspace = HDMI_COLORSPACE_RGB;
/* Set up colorimetry */
if (hdmi->hdmi_data.enc_out_format == XVYCC444) {
- colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO;
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
- ext_colorimetry =
- HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/
- ext_colorimetry =
- HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709;
+ frame.extended_colorimetry =
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
} else if (hdmi->hdmi_data.enc_out_format != RGB) {
- if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
- colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_SMPTE;
- else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/
- colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_ITUR;
- ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+ frame.colorimetry = hdmi->hdmi_data.colorimetry;
+ frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
} else { /* Carries no data */
- colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA;
- ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+ frame.colorimetry = HDMI_COLORIMETRY_NONE;
+ frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
}
- val = colorimetry | coded_ratio | act_ratio;
+ frame.scan_mode = HDMI_SCAN_MODE_NONE;
+
+ /*
+ * The Designware IP uses a different byte format from standard
+ * AVI info frames, though generally the bits are in the correct
+ * bytes.
+ */
+
+ /*
+ * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6,
+ * active aspect present in bit 6 rather than 4.
+ */
+ val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3);
+ if (frame.active_aspect & 15)
+ val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
+ if (frame.top_bar || frame.bottom_bar)
+ val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
+ if (frame.left_bar || frame.right_bar)
+ val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
+ hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+
+ /* AVI data byte 2 differences: none */
+ val = ((frame.colorimetry & 0x3) << 6) |
+ ((frame.picture_aspect & 0x3) << 4) |
+ (frame.active_aspect & 0xf);
hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
- /* AVI Data Byte 3 */
- val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
- HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
- HDMI_FC_AVICONF2_SCALING_NONE;
+ /* AVI data byte 3 differences: none */
+ val = ((frame.extended_colorimetry & 0x7) << 4) |
+ ((frame.quantization_range & 0x3) << 2) |
+ (frame.nups & 0x3);
+ if (frame.itc)
+ val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
- /* AVI Data Byte 4 */
- hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID);
+ /* AVI data byte 4 differences: none */
+ val = frame.video_code & 0x7f;
+ hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
/* AVI Data Byte 5- set up input and output pixel repetition */
val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
@@ -999,20 +1038,23 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi)
HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
- /* IT Content and quantization range = don't care */
- val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
- HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
+ /*
+ * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
+ * ycc range in bits 2,3 rather than 6,7
+ */
+ val = ((frame.ycc_quantization_range & 0x3) << 2) |
+ (frame.content_type & 0x3);
hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
/* AVI Data Bytes 6-13 */
- hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0);
- hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1);
+ hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
+ hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
+ hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
+ hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
+ hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
+ hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
+ hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
+ hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
}
static void hdmi_av_composer(struct dw_hdmi *hdmi,
@@ -1022,9 +1064,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
- vmode->mhsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC);
- vmode->mvsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC);
- vmode->minterlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
vmode->mpixelclock = mode->clock * 1000;
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
@@ -1034,13 +1073,13 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
- inv_val |= (vmode->mvsyncpolarity ?
+ inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
- HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
+ HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
- inv_val |= (vmode->mhsyncpolarity ?
+ inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
- HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
+ HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
inv_val |= (vmode->mdataenablepolarity ?
HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
@@ -1049,17 +1088,17 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
if (hdmi->vic == 39)
inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
else
- inv_val |= (vmode->minterlaced ?
+ inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
- HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
+ HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
- inv_val |= (vmode->minterlaced ?
+ inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
- HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
+ HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
- inv_val |= (vmode->mdvi ?
- HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
- HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+ inv_val |= hdmi->sink_is_hdmi ?
+ HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+ HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
@@ -1105,7 +1144,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
return;
dw_hdmi_phy_enable_tmds(hdmi, 0);
- dw_hdmi_phy_enable_power(hdmi, 0);
+ dw_hdmi_phy_enable_powerdown(hdmi, true);
hdmi->phy_enabled = false;
}
@@ -1186,10 +1225,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
if (!hdmi->vic) {
dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
- hdmi->hdmi_data.video_mode.mdvi = true;
} else {
dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
- hdmi->hdmi_data.video_mode.mdvi = false;
}
if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
@@ -1200,18 +1237,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
else
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
- if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
- (hdmi->vic == 12) || (hdmi->vic == 13) ||
- (hdmi->vic == 14) || (hdmi->vic == 15) ||
- (hdmi->vic == 25) || (hdmi->vic == 26) ||
- (hdmi->vic == 27) || (hdmi->vic == 28) ||
- (hdmi->vic == 29) || (hdmi->vic == 30) ||
- (hdmi->vic == 35) || (hdmi->vic == 36) ||
- (hdmi->vic == 37) || (hdmi->vic == 38))
- hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
- else
- hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
/* TODO: Get input format from IPU (via FB driver interface) */
@@ -1235,18 +1261,22 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
/* HDMI Initialization Step B.3 */
dw_hdmi_enable_video_path(hdmi);
- /* not for DVI mode */
- if (hdmi->hdmi_data.video_mode.mdvi) {
- dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
- } else {
- dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
+ if (hdmi->sink_has_audio) {
+ dev_dbg(hdmi->dev, "sink has audio support\n");
/* HDMI Initialization Step E - Configure audio */
hdmi_clk_regenerator_update_pixel_clock(hdmi);
hdmi_enable_audio_clk(hdmi);
+ }
+
+ /* not for DVI mode */
+ if (hdmi->sink_is_hdmi) {
+ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
/* HDMI Initialization Step F - Configure AVI InfoFrame */
- hdmi_config_AVI(hdmi);
+ hdmi_config_AVI(hdmi, mode);
+ } else {
+ dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
}
hdmi_video_packetize(hdmi);
@@ -1255,7 +1285,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi_tx_hdcp_config(hdmi);
dw_hdmi_clear_overflow(hdmi);
- if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
+ if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
hdmi_enable_overflow_interrupts(hdmi);
return 0;
@@ -1348,10 +1378,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
{
struct dw_hdmi *hdmi = bridge->driver_private;
- dw_hdmi_setup(hdmi, mode);
+ mutex_lock(&hdmi->mutex);
/* Store the display mode for plugin/DKMS poweron events */
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+ mutex_unlock(&hdmi->mutex);
}
static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1365,14 +1397,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ mutex_lock(&hdmi->mutex);
+ hdmi->disabled = true;
dw_hdmi_poweroff(hdmi);
+ mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ mutex_lock(&hdmi->mutex);
dw_hdmi_poweron(hdmi);
+ hdmi->disabled = false;
+ mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1405,6 +1443,8 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
+ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
@@ -1423,6 +1463,10 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
struct dw_hdmi, connector);
enum drm_mode_status mode_status = MODE_OK;
+ /* We don't support double-clocked modes */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
if (hdmi->plat_data->mode_valid)
mode_status = hdmi->plat_data->mode_valid(connector, mode);
@@ -1489,21 +1533,21 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+ hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+ mutex_lock(&hdmi->mutex);
if (phy_int_pol & HDMI_PHY_HPD) {
dev_dbg(hdmi->dev, "EVENT=plugin\n");
- hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
- dw_hdmi_poweron(hdmi);
+ if (!hdmi->disabled)
+ dw_hdmi_poweron(hdmi);
} else {
dev_dbg(hdmi->dev, "EVENT=plugout\n");
- hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
- HDMI_PHY_POL0);
-
- dw_hdmi_poweroff(hdmi);
+ if (!hdmi->disabled)
+ dw_hdmi_poweroff(hdmi);
}
- drm_helper_hpd_irq_event(hdmi->connector.dev);
+ mutex_unlock(&hdmi->mutex);
+ drm_helper_hpd_irq_event(hdmi->bridge->dev);
}
hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
@@ -1570,8 +1614,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
hdmi->sample_rate = 48000;
hdmi->ratio = 100;
hdmi->encoder = encoder;
+ hdmi->disabled = true;
+ mutex_init(&hdmi->mutex);
mutex_init(&hdmi->audio_mutex);
+ spin_lock_init(&hdmi->audio_lock);
of_property_read_u32(np, "reg-io-width", &val);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..ee7f7ed2ab12 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -7,8 +7,8 @@
* (at your option) any later version.
*/
-#ifndef __IMX_HDMI_H__
-#define __IMX_HDMI_H__
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
/* Identification Registers */
#define HDMI_DESIGN_ID 0x0000
@@ -525,7 +525,7 @@
/* I2C Master Registers (E-DDC) */
#define HDMI_I2CM_SLAVE 0x7E00
-#define HDMI_I2CMESS 0x7E01
+#define HDMI_I2CM_ADDRESS 0x7E01
#define HDMI_I2CM_DATAO 0x7E02
#define HDMI_I2CM_DATAI 0x7E03
#define HDMI_I2CM_OPERATION 0x7E04
@@ -1031,4 +1031,4 @@ enum {
HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
};
-#endif /* __IMX_HDMI_H__ */
+#endif /* __DW_HDMI_H__ */
diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index a3ecf1069b76..644edf65dbe0 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -75,6 +75,11 @@ static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = {
},
};
+/*
+ * Resistance term 133Ohm Cfg
+ * PREEMP config 0.00
+ * TX/CK level 10
+ */
static const struct dw_hdmi_phy_config imx_phy_config[] = {
/*pixelclk symbol term vlev */
{ 148500000, 0x800d, 0x0005, 0x01ad},
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index de13bfc35634..bae79f3c4d28 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -12,6 +12,8 @@
#include <drm/drmP.h>
+struct dw_hdmi;
+
enum {
DW_HDMI_RES_8,
DW_HDMI_RES_10,
@@ -59,4 +61,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
void *data, struct drm_encoder *encoder,
struct resource *iores, int irq,
const struct dw_hdmi_plat_data *plat_data);
+
+void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi);
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
+
#endif /* __IMX_HDMI_H__ */