From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 04 Jan 2024 10:08:02 +0000
Subject: [PATCH] disable FB
---
kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1640 ++++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 1,069 insertions(+), 571 deletions(-)
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 7ba60fb..5d20a72 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1,49 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DesignWare High-Definition Multimedia Interface (HDMI) driver
*
* Copyright (C) 2013-2015 Mentor Graphics Inc.
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
*/
-#include <linux/module.h>
-#include <linux/irq.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
-#include <linux/clk.h>
#include <linux/hdmi.h>
+#include <linux/irq.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/pinctrl/consumer.h>
-#include <drm/drm_of.h>
-#include <drm/drmP.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_encoder_slave.h>
-#include <drm/drm_scdc_helper.h>
-#include <drm/bridge/dw_hdmi.h>
+#include <media/cec-notifier.h>
#include <uapi/linux/media-bus-format.h>
#include <uapi/linux/videodev2.h>
-#include "dw-hdmi.h"
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_scdc_helper.h>
+
#include "dw-hdmi-audio.h"
#include "dw-hdmi-cec.h"
#include "dw-hdmi-hdcp.h"
-
-#include <media/cec-notifier.h>
+#include "dw-hdmi.h"
#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30
@@ -150,56 +147,20 @@
static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
{ 0x2591, 0x1322, 0x074b, 0x0000 },
- { 0xe535, 0x2000, 0xfacc, 0x0200 },
- { 0xeacd, 0xf534, 0x2000, 0x0200 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu601_10bit[3][4] = {
- { 0x2591, 0x1322, 0x074b, 0x0000 },
- { 0xe535, 0x2000, 0xfacc, 0x0800 },
- { 0xeacd, 0xf534, 0x2000, 0x0800 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu601_limited[3][4] = {
- { 0x2044, 0x106f, 0x0644, 0x0040 },
- { 0xe677, 0x1c1c, 0xfd46, 0x0200 },
- { 0xed60, 0xf685, 0x1c1c, 0x0200 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu601_10bit_limited[3][4] = {
- { 0x2044, 0x106f, 0x0644, 0x0100 },
- { 0xe677, 0x1c1c, 0xfd46, 0x0800 },
- { 0xed60, 0xf685, 0x1c1c, 0x0800 }
+ { 0x6535, 0x2000, 0x7acc, 0x0200 },
+ { 0x6acd, 0x7534, 0x2000, 0x0200 }
};
static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
{ 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
- { 0xe2f0, 0x2000, 0xfd11, 0x0200 },
- { 0xe756, 0xf8ab, 0x2000, 0x0200 }
+ { 0x62f0, 0x2000, 0x7d11, 0x0200 },
+ { 0x6756, 0x78ab, 0x2000, 0x0200 }
};
-static const u16 csc_coeff_rgb_in_eitu709_10bit[3][4] = {
- { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
- { 0xe2f0, 0x2000, 0xfd11, 0x0800 },
- { 0xe756, 0xf8ab, 0x2000, 0x0800 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu709_limited[3][4] = {
- { 0x2750, 0x0baf, 0x03f8, 0x0040 },
- { 0xe677, 0x1c1c, 0xfd6d, 0x0200 },
- { 0xea55, 0xf98f, 0x1c1c, 0x0200 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu709_10bit_limited[3][4] = {
- { 0x2750, 0x0baf, 0x03f8, 0x0100 },
- { 0xe677, 0x1c1c, 0xfd6d, 0x0800 },
- { 0xea55, 0xf98f, 0x1c1c, 0x0800 }
-};
-
-static const u16 csc_coeff_full_to_limited[3][4] = {
- { 0x36f7, 0x0000, 0x0000, 0x0040 },
- { 0x0000, 0x36f7, 0x0000, 0x0040 },
- { 0x0000, 0x0000, 0x36f7, 0x0040 }
+static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
+ { 0x1b7c, 0x0000, 0x0000, 0x0020 },
+ { 0x0000, 0x1b7c, 0x0000, 0x0020 },
+ { 0x0000, 0x0000, 0x1b7c, 0x0020 }
};
static const struct drm_display_mode dw_hdmi_default_modes[] = {
@@ -207,36 +168,32 @@
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
1430, 1650, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
- .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 16 - 1920x1080@60Hz 16:9 */
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
- .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 31 - 1920x1080@50Hz 16:9 */
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
- .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 19 - 1280x720@50Hz 16:9 */
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
1760, 1980, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
- .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 0x10 - 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 17 - 720x576@50Hz 4:3 */
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
796, 864, 0, 576, 581, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
- .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
/* 2 - 720x480@60Hz 4:3 */
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
798, 858, 0, 480, 489, 495, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
- .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
};
struct hdmi_vmode {
@@ -258,7 +215,7 @@
unsigned int quant_range;
unsigned int pix_repet_factor;
struct hdmi_vmode video_mode;
- bool update;
+ bool rgb_limited_range;
};
struct dw_hdmi_i2c {
@@ -304,6 +261,7 @@
struct hdmi_data_info hdmi_data;
const struct dw_hdmi_plat_data *plat_data;
+ const struct dw_hdmi_cec_wake_ops *cec_ops;
struct dw_hdcp *hdcp;
int vic;
@@ -321,7 +279,6 @@
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;
@@ -333,8 +290,13 @@
struct delayed_work work;
struct workqueue_struct *workqueue;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *default_state;
+ struct pinctrl_state *unwedge_state;
+
struct mutex mutex; /* for state below and previous_mode */
enum drm_connector_force force; /* mutex-protected force state */
+ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
bool disabled; /* DRM has disabled our bridge */
bool bridge_is_on; /* indicates the bridge is on */
bool rxsense; /* rxsense state */
@@ -357,14 +319,15 @@
void (*enable_audio)(struct dw_hdmi *hdmi);
void (*disable_audio)(struct dw_hdmi *hdmi);
+ struct mutex cec_notifier_mutex;
struct cec_notifier *cec_notifier;
+ struct cec_adapter *cec_adap;
- 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 initialized; /* hdmi is enabled before bind */
+ bool logo_plug_out; /* hdmi is plug out when kernel logo */
bool update;
bool hdr2sdr; /* from hdr to sdr */
};
@@ -446,6 +409,8 @@
static void repo_hpd_event(struct work_struct *p_work)
{
struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work);
+ enum drm_connector_status status = hdmi->hpd_state ?
+ connector_status_connected : connector_status_disconnected;
u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
mutex_lock(&hdmi->mutex);
@@ -457,16 +422,19 @@
if (hdmi->bridge.dev) {
bool change;
+ void *data = hdmi->plat_data->phy_data;
change = drm_helper_hpd_irq_event(hdmi->bridge.dev);
-#ifdef CONFIG_CEC_NOTIFIER
if (change) {
- cec_notifier_repo_cec_hpd(hdmi->cec_notifier,
- hdmi->hpd_state,
- ktime_get());
+ if (hdmi->plat_data->set_ddc_io)
+ hdmi->plat_data->set_ddc_io(data, hdmi->hpd_state);
+ if (hdmi->cec_adap->devnode.registered)
+ cec_queue_pin_hpd_event(hdmi->cec_adap,
+ hdmi->hpd_state,
+ ktime_get());
}
-#endif
+ drm_bridge_hpd_notify(&hdmi->bridge, status);
}
}
@@ -544,6 +512,13 @@
static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
{
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
/* Software reset */
hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
@@ -564,17 +539,90 @@
hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
HDMI_IH_MUTE_I2CM_STAT0);
- /* set SDA high level holding time */
- hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
+ /* Only configure when we use the internal I2C controller */
+ if (hdmi->i2c) {
+ /* set SDA high level holding time */
+ hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
+ dw_hdmi_i2c_set_divs(hdmi);
+ }
+}
- dw_hdmi_i2c_set_divs(hdmi);
+static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi)
+{
+ /* If no unwedge state then give up */
+ if (!hdmi->unwedge_state)
+ return false;
+
+ dev_info(hdmi->dev, "Attempting to unwedge stuck i2c bus\n");
+
+ /*
+ * This is a huge hack to workaround a problem where the dw_hdmi i2c
+ * bus could sometimes get wedged. Once wedged there doesn't appear
+ * to be any way to unwedge it (including the HDMI_I2CM_SOFTRSTZ)
+ * other than pulsing the SDA line.
+ *
+ * We appear to be able to pulse the SDA line (in the eyes of dw_hdmi)
+ * by:
+ * 1. Remux the pin as a GPIO output, driven low.
+ * 2. Wait a little while. 1 ms seems to work, but we'll do 10.
+ * 3. Immediately jump to remux the pin as dw_hdmi i2c again.
+ *
+ * At the moment of remuxing, the line will still be low due to its
+ * recent stint as an output, but then it will be pulled high by the
+ * (presumed) external pullup. dw_hdmi seems to see this as a rising
+ * edge and that seems to get it out of its jam.
+ *
+ * This wedging was only ever seen on one TV, and only on one of
+ * its HDMI ports. It happened when the TV was powered on while the
+ * device was plugged in. A scope trace shows the TV bringing both SDA
+ * and SCL low, then bringing them both back up at roughly the same
+ * time. Presumably this confuses dw_hdmi because it saw activity but
+ * no real STOP (maybe it thinks there's another master on the bus?).
+ * Giving it a clean rising edge of SDA while SCL is already high
+ * presumably makes dw_hdmi see a STOP which seems to bring dw_hdmi out
+ * of its stupor.
+ *
+ * Note that after coming back alive, transfers seem to immediately
+ * resume, so if we unwedge due to a timeout we should wait a little
+ * longer for our transfer to finish, since it might have just started
+ * now.
+ */
+ pinctrl_select_state(hdmi->pinctrl, hdmi->unwedge_state);
+ msleep(10);
+ pinctrl_select_state(hdmi->pinctrl, hdmi->default_state);
+
+ return true;
+}
+
+static int dw_hdmi_i2c_wait(struct dw_hdmi *hdmi)
+{
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
+ int stat;
+
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat) {
+ /* If we can't unwedge, return timeout */
+ if (!dw_hdmi_i2c_unwedge(hdmi))
+ return -EAGAIN;
+
+ /* We tried to unwedge; give it another chance */
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat)
+ return -EAGAIN;
+ }
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
+ return -EIO;
+
+ return 0;
}
static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
unsigned char *buf, unsigned int length)
{
struct dw_hdmi_i2c *i2c = hdmi->i2c;
- int stat, retry, i;
+ int ret, retry, i;
bool read_edid = false;
if (!i2c->is_regaddr) {
@@ -601,14 +649,10 @@
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,
@@ -624,17 +668,16 @@
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
HDMI_I2CM_OPERATION);
}
- stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
- if (!stat) {
+
+ ret = dw_hdmi_i2c_wait(hdmi);
+ if (ret == -EAGAIN) {
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) {
+ } else if (ret == -EIO) {
dev_dbg(hdmi->dev, "ddc read err\n");
hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
@@ -667,7 +710,7 @@
unsigned char *buf, unsigned int length)
{
struct dw_hdmi_i2c *i2c = hdmi->i2c;
- int stat, retry;
+ int ret, retry;
if (!i2c->is_regaddr) {
/* Use the first write byte as register address */
@@ -685,29 +728,23 @@
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 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) {
+ ret = dw_hdmi_i2c_wait(hdmi);
+ if (ret == -EAGAIN) {
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) {
+ } else if (ret == -EIO) {
dev_dbg(hdmi->dev, "ddc write err\n");
hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
@@ -736,7 +773,6 @@
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)
@@ -760,9 +796,6 @@
}
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);
@@ -866,8 +899,14 @@
/* nshift factor = 0 */
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
- HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+ /* Use automatic CTS generation mode when CTS is not set */
+ if (cts)
+ hdmi_writeb(hdmi, ((cts >> 16) &
+ HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+ HDMI_AUD_CTS3_CTS_MANUAL,
+ HDMI_AUD_CTS3);
+ else
+ hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3);
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
@@ -989,29 +1028,58 @@
return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
}
+/*
+ * When transmitting IEC60958 linear PCM audio, these registers allow to
+ * configure the channel status information of all the channel status
+ * bits in the IEC60958 frame. For the moment this configuration is only
+ * used when the I2S audio interface, General Purpose Audio (GPA),
+ * or AHB audio DMA (AHBAUDDMA) interface is active
+ * (for S/PDIF interface this information comes from the stream).
+ */
+void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi,
+ u8 *channel_status)
+{
+ /*
+ * Set channel status register for frequency and word length.
+ * Use default values for other registers.
+ */
+ hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7);
+ hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status);
+
static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
unsigned long pixel_clk, unsigned int sample_rate)
{
unsigned long ftdms = pixel_clk;
unsigned int n, cts;
+ u8 config3;
u64 tmp;
n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
- /*
- * Compute the CTS value from the N value. Note that CTS and N
- * can be up to 20 bits in total, so we need 64-bit math. Also
- * note that our TDMS clock is not fully accurate; it is accurate
- * to kHz. This can introduce an unnecessary remainder in the
- * calculation below, so we don't try to warn about that.
- */
- tmp = (u64)ftdms * n;
- do_div(tmp, 128 * sample_rate);
- cts = tmp;
+ config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
- dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
- __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
- n, cts);
+ /* Only compute CTS when using internal AHB audio */
+ if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
+ /*
+ * Compute the CTS value from the N value. Note that CTS and N
+ * can be up to 20 bits in total, so we need 64-bit math. Also
+ * note that our TDMS clock is not fully accurate; it is
+ * accurate to kHz. This can introduce an unnecessary remainder
+ * in the calculation below, so we don't try to warn about that.
+ */
+ tmp = (u64)ftdms * n;
+ do_div(tmp, 128 * sample_rate);
+ cts = tmp;
+
+ dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+ __func__, sample_rate,
+ ftdms / 1000000, (ftdms / 1000) % 1000,
+ n, cts);
+ } else {
+ cts = 0;
+ }
spin_lock_irq(&hdmi->audio_lock);
hdmi->audio_n = n;
@@ -1045,6 +1113,42 @@
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
+{
+ u8 layout;
+
+ mutex_lock(&hdmi->audio_mutex);
+
+ /*
+ * For >2 channel PCM audio, we need to select layout 1
+ * and set an appropriate channel map.
+ */
+ if (cnt > 2)
+ layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1;
+ else
+ layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0;
+
+ hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK,
+ HDMI_FC_AUDSCONF);
+
+ /* Set the audio infoframes channel count */
+ hdmi_modb(hdmi, (cnt - 1) << HDMI_FC_AUDICONF0_CC_OFFSET,
+ HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0);
+
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count);
+
+void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca)
+{
+ mutex_lock(&hdmi->audio_mutex);
+
+ hdmi_writeb(hdmi, ca, HDMI_FC_AUDICONF2);
+
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_allocation);
+
static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
{
if (enable)
@@ -1052,6 +1156,14 @@
else
hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE;
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
+{
+ if (!hdmi->curr_conn)
+ return NULL;
+
+ return hdmi->curr_conn->eld;
}
static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
@@ -1262,25 +1374,14 @@
static int is_color_space_conversion(struct dw_hdmi *hdmi)
{
- const struct drm_display_mode mode = hdmi->previous_mode;
- bool is_cea_default;
+ struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+ bool is_input_rgb, is_output_rgb;
- is_cea_default = (drm_match_cea_mode(&mode) > 1) &&
- (hdmi->hdmi_data.quant_range ==
- HDMI_QUANTIZATION_RANGE_DEFAULT);
+ is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format);
+ is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format);
- /*
- * When output is rgb limited range or default range with
- * cea mode, csc should be enabled.
- */
- if (hdmi->hdmi_data.enc_in_bus_format !=
- hdmi->hdmi_data.enc_out_bus_format ||
- ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
- is_cea_default) &&
- hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format)))
- return 1;
-
- return 0;
+ return (is_input_rgb != is_output_rgb) ||
+ (is_input_rgb && is_output_rgb && hdmi_data->rgb_limited_range);
}
static int is_color_space_decimation(struct dw_hdmi *hdmi)
@@ -1307,43 +1408,46 @@
return 0;
}
+static bool is_csc_needed(struct dw_hdmi *hdmi)
+{
+ return is_color_space_conversion(hdmi) ||
+ is_color_space_decimation(hdmi) ||
+ is_color_space_interpolation(hdmi);
+}
+
+static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi)
+{
+ if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
+ (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range))
+ return true;
+
+ return false;
+}
+
static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
{
const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
+ bool is_input_rgb, is_output_rgb;
unsigned i;
u32 csc_scale = 1;
- int enc_out_rgb, enc_in_rgb;
- int color_depth;
- enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
- enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
- color_depth = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+ is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
+ is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
- if (is_color_space_conversion(hdmi)) {
- if (enc_out_rgb && enc_in_rgb) {
- csc_coeff = &csc_coeff_full_to_limited;
- csc_scale = 0;
- } else if (enc_out_rgb) {
- if (hdmi->hdmi_data.enc_out_encoding ==
- V4L2_YCBCR_ENC_601)
- csc_coeff = &csc_coeff_rgb_out_eitu601;
- else
- csc_coeff = &csc_coeff_rgb_out_eitu709;
- } else if (enc_in_rgb) {
- if (hdmi->hdmi_data.enc_out_encoding ==
- V4L2_YCBCR_ENC_601) {
- if (color_depth == 10)
- csc_coeff = &csc_coeff_rgb_in_eitu601_10bit_limited;
- else
- csc_coeff = &csc_coeff_rgb_in_eitu601_limited;
- } else {
- if (color_depth == 10)
- csc_coeff = &csc_coeff_rgb_in_eitu709_10bit_limited;
- else
- csc_coeff = &csc_coeff_rgb_in_eitu709_limited;
- }
- csc_scale = 0;
- }
+ if (!is_input_rgb && is_output_rgb) {
+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601)
+ csc_coeff = &csc_coeff_rgb_out_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_out_eitu709;
+ } else if (is_input_rgb && !is_output_rgb) {
+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601)
+ csc_coeff = &csc_coeff_rgb_in_eitu601;
+ else
+ csc_coeff = &csc_coeff_rgb_in_eitu709;
+ csc_scale = 0;
+ } else if (is_input_rgb && is_output_rgb &&
+ is_rgb_full_to_limited_needed(hdmi)) {
+ csc_coeff = &csc_coeff_rgb_full_to_rgb_limited;
}
/* The CSC registers are sequential, alternating MSB then LSB */
@@ -1551,30 +1655,16 @@
}
EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
-static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
-{
- int val;
-
- hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
- hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
- hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
- hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
- hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
- HDMI_PHY_I2CM_OPERATION_ADDR);
- hdmi_phy_wait_i2c_done(hdmi, 1000);
- val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
- val = (val & 0xff) << 8;
- val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
- return val;
-}
-
/* Filter out invalid setups to avoid configuring SCDC and scrambling */
-static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi)
+static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
- struct drm_display_info *display = &hdmi->connector.display_info;
-
/* Completely disable SCDC support for older controllers */
if (hdmi->version < 0x200a)
+ return false;
+
+ /* Disable if no DDC bus */
+ if (!hdmi->ddc)
return false;
/* Disable if SCDC is not supported, or if an HF-VSDB block is absent */
@@ -1593,6 +1683,23 @@
return true;
}
+static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
+{
+ int val;
+
+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
+ HDMI_PHY_I2CM_OPERATION_ADDR);
+ hdmi_phy_wait_i2c_done(hdmi, 1000);
+ val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ val = (val & 0xff) << 8;
+ val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
+ return val;
+}
+
/*
* HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
* - The Source shall suspend transmission of the TMDS clock and data
@@ -1606,12 +1713,13 @@
* helper should called right before enabling the TMDS Clock and Data in
* the PHY configuration callback.
*/
-void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
+void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
- if (dw_hdmi_support_scdc(hdmi)) {
+ if (dw_hdmi_support_scdc(hdmi, display)) {
if (mtmdsclock > HDMI14_MAX_TMDSCLK)
drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1);
else
@@ -1824,7 +1932,8 @@
return 0;
}
-static int hdmi_phy_configure(struct dw_hdmi *hdmi)
+static int hdmi_phy_configure(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
@@ -1834,7 +1943,7 @@
dw_hdmi_phy_power_off(hdmi);
- dw_hdmi_set_high_tmds_clock_ratio(hdmi);
+ dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
/* Leave low power consumption mode by asserting SVSRET. */
if (phy->has_svsret)
@@ -1848,7 +1957,7 @@
/* Write to the PHY as configured by the platform */
if (pdata->configure_phy)
- ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
+ ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
else
ret = phy->configure(hdmi, pdata, mpixelclock);
if (ret) {
@@ -1865,7 +1974,8 @@
}
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
- struct drm_display_mode *mode)
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
{
int i, ret;
@@ -1874,7 +1984,7 @@
dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
dw_hdmi_phy_sel_interface_control(hdmi, 0);
- ret = hdmi_phy_configure(hdmi);
+ ret = hdmi_phy_configure(hdmi, display);
if (ret)
return ret;
}
@@ -1977,23 +2087,35 @@
hdmi->hdcp->hdcp_start(hdmi->hdcp);
}
-static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+static void hdmi_config_AVI(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
struct hdmi_avi_infoframe frame;
u8 val;
- bool is_hdmi2 = false;
- enum hdmi_quantization_range rgb_quant_range =
- hdmi->hdmi_data.quant_range;
+ bool is_hdmi2;
+ const struct drm_display_info *info = &connector->display_info;
- if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
- hdmi->connector.display_info.hdmi.scdc.supported)
- is_hdmi2 = true;
+ is_hdmi2 = info->hdmi.scdc.supported || (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
+
/* Initialise info frame from DRM mode */
- drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2);
+ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
- drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range,
- hdmi->rgb_quant_range_selectable || is_hdmi2,
- is_hdmi2);
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
+ /* default range */
+ if (!hdmi->hdmi_data.quant_range)
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+ hdmi->hdmi_data.rgb_limited_range ?
+ HDMI_QUANTIZATION_RANGE_LIMITED :
+ HDMI_QUANTIZATION_RANGE_FULL);
+ else
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+ hdmi->hdmi_data.quant_range);
+ } else {
+ frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+ frame.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ }
if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
frame.colorspace = HDMI_COLORSPACE_YUV444;
@@ -2029,8 +2151,8 @@
else
frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
frame.extended_colorimetry =
- HDMI_EXTENDED_COLORIMETRY_BT2020;
- break;
+ HDMI_EXTENDED_COLORIMETRY_BT2020;
+ break;
default: /* Carries no data */
frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
frame.extended_colorimetry =
@@ -2054,8 +2176,6 @@
else
frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
}
-
- frame.scan_mode = HDMI_SCAN_MODE_NONE;
/*
* The Designware IP uses a different byte format from standard
@@ -2128,7 +2248,8 @@
}
static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
- struct drm_display_mode *mode)
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
struct hdmi_vendor_infoframe frame;
u8 buffer[10];
@@ -2189,6 +2310,77 @@
HDMI_FC_DATAUTO0_VSD_MASK);
}
+static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector)
+{
+ const struct drm_connector_state *conn_state = connector->state;
+ struct hdr_output_metadata *hdr_metadata;
+ struct hdmi_drm_infoframe frame;
+ u8 buffer[30];
+ ssize_t err;
+ int i;
+
+ /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
+ if (hdmi->version < 0x211a) {
+ dev_dbg(hdmi->dev, "Not support DRM Infoframe\n");
+ return;
+ }
+
+ if (!hdmi->plat_data->use_drm_infoframe)
+ return;
+
+ hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
+ HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+
+ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
+ DRM_DEBUG("No need to set HDR metadata in infoframe\n");
+ return;
+ }
+
+ if (!conn_state->hdr_output_metadata) {
+ DRM_DEBUG("source metadata not set yet\n");
+ return;
+ }
+
+ hdr_metadata = (struct hdr_output_metadata *)
+ conn_state->hdr_output_metadata->data;
+
+ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf &
+ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) {
+ DRM_ERROR("Not support EOTF %d\n",
+ hdr_metadata->hdmi_metadata_type1.eotf);
+ return;
+ }
+
+ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
+ if (err < 0)
+ return;
+
+ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err);
+ return;
+ }
+
+ hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0);
+ hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1);
+
+ for (i = 0; i < frame.length; i++)
+ hdmi_writeb(hdmi, buffer[4 + i], HDMI_FC_DRM_PB0 + i);
+
+ 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_TX_EN_DRM_ENABLE,
+ HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+
+ DRM_DEBUG("%s eotf %d end\n", __func__,
+ hdr_metadata->hdmi_metadata_type1.eotf);
+}
+
static unsigned int
hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
{
@@ -2215,129 +2407,25 @@
return tmdsclock;
}
-#define HDR_LSB(n) ((n) & 0xff)
-#define HDR_MSB(n) (((n) & 0xff00) >> 8)
-
-/* Set Dynamic Range and Mastering Infoframe */
-static void hdmi_config_hdr_infoframe(struct dw_hdmi *hdmi)
-{
- struct hdmi_drm_infoframe frame;
- struct hdr_output_metadata *hdr_metadata;
- struct drm_connector_state *conn_state = hdmi->connector.state;
- int ret;
-
- /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
- if (hdmi->version < 0x211a) {
- DRM_ERROR("Not support DRM Infoframe\n");
- return;
- }
-
- hdmi_modb(hdmi, HDMI_FC_PACKET_DRM_TX_DEN,
- HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN);
-
- if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
- DRM_DEBUG("No need to set HDR metadata in infoframe\n");
- return;
- }
-
- if (!conn_state->hdr_output_metadata) {
- DRM_DEBUG("source metadata not set yet\n");
- return;
- }
-
- hdr_metadata = (struct hdr_output_metadata *)
- conn_state->hdr_output_metadata->data;
-
- if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf &
- BIT(hdr_metadata->hdmi_metadata_type1.eotf))) {
- DRM_ERROR("Not support EOTF %d\n",
- hdr_metadata->hdmi_metadata_type1.eotf);
- return;
- }
-
- ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
- if (ret < 0) {
- DRM_ERROR("couldn't set HDR metadata in infoframe\n");
- return;
- }
-
- hdmi_writeb(hdmi, 1, HDMI_FC_DRM_HB0);
- hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1);
- hdmi_writeb(hdmi, frame.eotf, HDMI_FC_DRM_PB0);
- hdmi_writeb(hdmi, frame.metadata_type, HDMI_FC_DRM_PB1);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].x),
- HDMI_FC_DRM_PB2);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].x),
- HDMI_FC_DRM_PB3);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].y),
- HDMI_FC_DRM_PB4);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].y),
- HDMI_FC_DRM_PB5);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].x),
- HDMI_FC_DRM_PB6);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].x),
- HDMI_FC_DRM_PB7);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].y),
- HDMI_FC_DRM_PB8);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].y),
- HDMI_FC_DRM_PB9);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].x),
- HDMI_FC_DRM_PB10);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].x),
- HDMI_FC_DRM_PB11);
- hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].y),
- HDMI_FC_DRM_PB12);
- hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].y),
- HDMI_FC_DRM_PB13);
- hdmi_writeb(hdmi, HDR_LSB(frame.white_point.x), HDMI_FC_DRM_PB14);
- hdmi_writeb(hdmi, HDR_MSB(frame.white_point.x), HDMI_FC_DRM_PB15);
- hdmi_writeb(hdmi, HDR_LSB(frame.white_point.y), HDMI_FC_DRM_PB16);
- hdmi_writeb(hdmi, HDR_MSB(frame.white_point.y), HDMI_FC_DRM_PB17);
- hdmi_writeb(hdmi, HDR_LSB(frame.max_display_mastering_luminance),
- HDMI_FC_DRM_PB18);
- hdmi_writeb(hdmi, HDR_MSB(frame.max_display_mastering_luminance),
- HDMI_FC_DRM_PB19);
- hdmi_writeb(hdmi, HDR_LSB(frame.min_display_mastering_luminance),
- HDMI_FC_DRM_PB20);
- hdmi_writeb(hdmi, HDR_MSB(frame.min_display_mastering_luminance),
- HDMI_FC_DRM_PB21);
- hdmi_writeb(hdmi, HDR_LSB(frame.max_cll), HDMI_FC_DRM_PB22);
- hdmi_writeb(hdmi, HDR_MSB(frame.max_cll), HDMI_FC_DRM_PB23);
- 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);
-
- DRM_DEBUG("%s eotf %d end\n", __func__,
- hdr_metadata->hdmi_metadata_type1.eotf);
-}
-
static void hdmi_av_composer(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display,
const struct drm_display_mode *mode)
{
u8 inv_val, bytes;
- struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
+ const struct drm_hdmi_info *hdmi_info = &display->hdmi;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
unsigned int vdisplay, hdisplay;
vmode->previous_pixelclock = vmode->mpixelclock;
vmode->mpixelclock = mode->crtc_clock * 1000;
- if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
- DRM_MODE_FLAG_3D_FRAME_PACKING)
- vmode->mpixelclock *= 2;
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
vmode->previous_tmdsclock = vmode->mtmdsclock;
vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
vmode->mtmdsclock /= 2;
+ dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
if (hdmi->update)
return;
@@ -2408,13 +2496,10 @@
vblank /= 2;
v_de_vs /= 2;
vsync_len /= 2;
- } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
- DRM_MODE_FLAG_3D_FRAME_PACKING) {
- vdisplay += mode->vtotal;
}
/* Scrambling Control */
- if (dw_hdmi_support_scdc(hdmi)) {
+ if (dw_hdmi_support_scdc(hdmi, display)) {
if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
(hdmi_info->scdc.scrambling.low_rates &&
hdmi->scramble_low_rates)) {
@@ -2512,25 +2597,26 @@
hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
- /* Enable csc path */
- if (is_color_space_conversion(hdmi)) {
- hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
- hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
- }
-
/* Enable pixel repetition path */
if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
}
- /* Enable color space conversion if needed */
- if (is_color_space_conversion(hdmi))
+ /* Enable csc path */
+ if (is_csc_needed(hdmi)) {
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
HDMI_MC_FLOWCTRL);
- else
+ } else {
+ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+
hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
HDMI_MC_FLOWCTRL);
+ }
}
/* Workaround to clear the overflow condition */
@@ -2553,6 +2639,8 @@
* iteration for others.
* The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
* the workaround with a single iteration.
+ * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have
+ * been identified as needing the workaround with a single iteration.
*/
switch (hdmi->version) {
@@ -2560,9 +2648,11 @@
count = 4;
break;
case 0x131a:
+ case 0x132a:
case 0x200a:
case 0x201a:
case 0x211a:
+ case 0x212a:
count = 1;
break;
default:
@@ -2583,7 +2673,7 @@
HDMI_IH_MUTE_FC_STAT2);
}
-static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+static void dw_hdmi_force_output_pattern(struct dw_hdmi *hdmi, const struct drm_display_mode *mode)
{
/* force output black */
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
@@ -2615,7 +2705,9 @@
}
}
-static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+static int dw_hdmi_setup(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
int ret;
void *data = hdmi->plat_data->phy_data;
@@ -2680,11 +2772,14 @@
else
hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
+
if (hdmi->plat_data->get_quant_range)
hdmi->hdmi_data.quant_range =
hdmi->plat_data->get_quant_range(data);
- else
- hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+
+ hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi &&
+ drm_default_rgb_quant_range(mode) ==
+ HDMI_QUANTIZATION_RANGE_LIMITED;
if (!hdmi->sink_is_hdmi)
hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL;
@@ -2702,7 +2797,7 @@
dw_hdmi_force_output_pattern(hdmi, mode);
/* HDMI Initialization Step B.1 */
- hdmi_av_composer(hdmi, mode);
+ hdmi_av_composer(hdmi, &connector->display_info, mode);
/* HDMI Initialization Step B.3 */
dw_hdmi_enable_video_path(hdmi);
@@ -2720,9 +2815,9 @@
dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
/* HDMI Initialization Step F - Configure AVI InfoFrame */
- hdmi_config_AVI(hdmi, mode);
- hdmi_config_vendor_specific_infoframe(hdmi, mode);
- hdmi_config_hdr_infoframe(hdmi);
+ hdmi_config_AVI(hdmi, connector, mode);
+ hdmi_config_vendor_specific_infoframe(hdmi, connector, mode);
+ hdmi_config_drm_infoframe(hdmi, connector);
} else {
dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
}
@@ -2739,6 +2834,7 @@
hdmi->hdmi_data.video_mode.previous_tmdsclock !=
hdmi->hdmi_data.video_mode.mtmdsclock) {
ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
+ &connector->display_info,
&hdmi->previous_mode);
if (ret)
return ret;
@@ -2758,16 +2854,6 @@
}
return 0;
-}
-
-static void dw_hdmi_setup_i2c(struct dw_hdmi *hdmi)
-{
- hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
- HDMI_PHY_I2CM_INT_ADDR);
-
- hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
- HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
- HDMI_PHY_I2CM_CTLINT_ADDR);
}
static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
@@ -2824,7 +2910,12 @@
static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
{
hdmi->bridge_is_on = true;
- dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+
+ /*
+ * The curr_conn field is guaranteed to be valid here, as this function
+ * is only be called when !hdmi->disabled.
+ */
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
}
static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
@@ -2886,11 +2977,8 @@
hdmi->rxsense);
}
-static enum drm_connector_status
-dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
{
- struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
- connector);
enum drm_connector_status result;
if (!hdmi->force_logo) {
@@ -2902,20 +2990,56 @@
}
result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ mutex_lock(&hdmi->mutex);
+ if (result != hdmi->last_connector_result) {
+ dev_dbg(hdmi->dev, "read_hpd result: %d", result);
+ handle_plugged_change(hdmi,
+ result == connector_status_connected);
+ hdmi->last_connector_result = result;
+ }
+ mutex_unlock(&hdmi->mutex);
+
if (result == connector_status_connected)
extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
else
extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
- mutex_lock(&hdmi->mutex);
- if (result != hdmi->last_connector_result) {
- dev_dbg(hdmi->dev, "read_hpd result: %d", result);
- handle_plugged_change(hdmi,
- result == connector_status_connected);
- hdmi->last_connector_result = result;
- }
- mutex_unlock(&hdmi->mutex);
return result;
+}
+
+static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
+ struct drm_connector *connector)
+{
+ struct edid *edid;
+
+ if (!hdmi->ddc)
+ return NULL;
+
+ edid = drm_get_edid(connector, hdmi->ddc);
+ if (!edid) {
+ dev_dbg(hdmi->dev, "failed to get edid\n");
+ return NULL;
+ }
+
+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+ edid->width_cm, edid->height_cm);
+
+ hdmi->support_hdmi = drm_detect_hdmi_monitor(edid);
+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
+
+ return edid;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Connector Operations
+ */
+
+static enum drm_connector_status
+dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ return dw_hdmi_detect(hdmi);
}
static int
@@ -2956,25 +3080,23 @@
struct edid *edid;
struct drm_display_mode *mode;
struct drm_display_info *info = &connector->display_info;
- int i, ret = 0;
+ void *data = hdmi->plat_data->phy_data;
+ int i, ret = 0;
memset(metedata, 0, sizeof(*metedata));
- if (!hdmi->ddc)
- return 0;
-
- edid = drm_get_edid(connector, hdmi->ddc);
+ edid = dw_hdmi_get_edid(hdmi, connector);
if (edid) {
int vic = 0;
dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
-
- hdmi->support_hdmi = drm_detect_hdmi_monitor(edid);
- hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
- hdmi->rgb_quant_range_selectable = drm_rgb_quant_range_selectable(edid);
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);
+ if (hdmi->plat_data->get_color_changed)
+ hdmi->plat_data->get_yuv422_format(connector, edid);
+ if (hdmi->plat_data->get_colorimetry)
+ hdmi->plat_data->get_colorimetry(data, edid);
list_for_each_entry(mode, &connector->probed_modes, head) {
vic = drm_match_cea_mode(mode);
@@ -2991,8 +3113,6 @@
} else {
hdmi->support_hdmi = true;
hdmi->sink_has_audio = true;
- hdmi->rgb_quant_range_selectable = false;
-
for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) {
const struct drm_display_mode *ptr =
&dw_hdmi_default_modes[i];
@@ -3017,48 +3137,13 @@
return ret;
}
-static int
-dw_hdmi_atomic_connector_set_property(struct drm_connector *connector,
- struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t val)
+static struct drm_encoder *
+dw_hdmi_connector_best_encoder(struct drm_connector *connector)
{
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
- connector);
- const struct dw_hdmi_property_ops *ops =
- hdmi->plat_data->property_ops;
+ connector);
- if (ops && ops->set_property)
- return ops->set_property(connector, state, property,
- val, hdmi->plat_data->phy_data);
- else
- return -EINVAL;
-}
-
-static int
-dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val)
-{
- struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
- connector);
- const struct dw_hdmi_property_ops *ops =
- hdmi->plat_data->property_ops;
-
- if (ops && ops->get_property)
- return ops->get_property(connector, state, property,
- val, hdmi->plat_data->phy_data);
- else
- return -EINVAL;
-}
-
-static int
-dw_hdmi_connector_set_property(struct drm_connector *connector,
- struct drm_property *property, uint64_t val)
-{
- return dw_hdmi_atomic_connector_set_property(connector, NULL,
- property, val);
+ return hdmi->bridge.encoder;
}
static bool dw_hdmi_color_changed(struct drm_connector *connector)
@@ -3140,22 +3225,6 @@
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,
struct drm_atomic_state *state)
{
@@ -3187,6 +3256,8 @@
if (!vmode->mpixelclock) {
u8 val;
+ hdmi->curr_conn = connector;
+
if (hdmi->plat_data->get_enc_in_encoding)
hdmi->hdmi_data.enc_in_encoding =
hdmi->plat_data->get_enc_in_encoding(data);
@@ -3210,10 +3281,6 @@
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 */
@@ -3221,8 +3288,8 @@
hdmi->logo_plug_out = true;
}
- if (check_hdmi_format_change(old_state, new_state, connector, hdmi) ||
- hdmi->logo_plug_out) {
+ if (check_hdr_color_change(old_state, new_state, hdmi) || hdmi->logo_plug_out ||
+ dw_hdmi_color_changed(connector)) {
u32 mtmdsclk;
if (hdmi->plat_data->update_color_format)
@@ -3265,6 +3332,50 @@
return 0;
}
+static int
+dw_hdmi_atomic_connector_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ const struct dw_hdmi_property_ops *ops =
+ hdmi->plat_data->property_ops;
+
+ if (ops && ops->set_property)
+ return ops->set_property(connector, state, property,
+ val, hdmi->plat_data->phy_data);
+ else
+ return -EINVAL;
+}
+
+static int
+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ const struct dw_hdmi_property_ops *ops =
+ hdmi->plat_data->property_ops;
+
+ if (ops && ops->get_property)
+ return ops->get_property(connector, state, property,
+ val, hdmi->plat_data->phy_data);
+ else
+ return -EINVAL;
+}
+
+static int
+dw_hdmi_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t val)
+{
+ return dw_hdmi_atomic_connector_set_property(connector, NULL,
+ property, val);
+}
+
static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector,
struct drm_connector_state *state)
{
@@ -3272,7 +3383,7 @@
container_of(connector, struct dw_hdmi, connector);
if (hdmi->update) {
- dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
mdelay(50);
handle_plugged_change(hdmi, true);
hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
@@ -3286,7 +3397,7 @@
return;
hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
- dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range);
@@ -3302,7 +3413,7 @@
return;
hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
- dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type);
@@ -3324,7 +3435,11 @@
if (!hdmi->cec)
return;
- dw_hdmi_hpd_wake_up(hdmi->cec);
+ if (!hdmi->cec_ops)
+ return;
+
+ if (hdmi->cec_ops->hpd_wake_up)
+ hdmi->cec_ops->hpd_wake_up(hdmi->cec);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_hpd_wake);
@@ -3365,7 +3480,7 @@
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
.get_modes = dw_hdmi_connector_get_modes,
- .best_encoder = drm_atomic_helper_best_encoder,
+ .best_encoder = dw_hdmi_connector_best_encoder,
.atomic_check = dw_hdmi_connector_atomic_check,
.atomic_commit = dw_hdmi_connector_atomic_commit,
};
@@ -3436,7 +3551,7 @@
if (ops && ops->attach_properties)
return ops->attach_properties(&hdmi->connector,
color, hdmi->version,
- hdmi->plat_data->phy_data);
+ hdmi->plat_data->phy_data, 0);
}
static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi)
@@ -3449,47 +3564,393 @@
hdmi->plat_data->phy_data);
}
-static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
+static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
{
- struct dw_hdmi *hdmi = bridge->driver_private;
- struct drm_encoder *encoder = bridge->encoder;
struct drm_connector *connector = &hdmi->connector;
- int ret;
+ struct cec_connector_info conn_info;
+ struct cec_notifier *notifier;
- if (!hdmi->next_bridge) {
- connector->interlace_allowed = 1;
- connector->polled = DRM_CONNECTOR_POLL_HPD;
+ if (hdmi->version >= 0x200a)
+ connector->ycbcr_420_allowed =
+ hdmi->plat_data->ycbcr_420_allowed;
+ else
+ connector->ycbcr_420_allowed = false;
- drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
- drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA);
+ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
- drm_connector_attach_encoder(connector, encoder);
+ drm_connector_init_with_ddc(hdmi->bridge.dev, connector,
+ &dw_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ hdmi->ddc);
- dw_hdmi_attach_properties(hdmi);
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ drm_atomic_helper_connector_reset(connector);
- return 0;
- }
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
- hdmi->next_bridge->encoder = bridge->encoder;
- ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge);
- if (ret) {
- DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
- return ret;
- }
+ if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
+ drm_object_attach_property(&connector->base,
+ connector->dev->mode_config.hdr_output_metadata_property, 0);
- bridge->next = hdmi->next_bridge;
+ drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
+
+ dw_hdmi_attach_properties(hdmi);
+
+ cec_fill_conn_info_from_drm(&conn_info, connector);
+
+ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
+ if (!notifier)
+ return -ENOMEM;
+
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ hdmi->cec_notifier = notifier;
+ mutex_unlock(&hdmi->cec_notifier_mutex);
return 0;
}
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
+/*
+ * Possible output formats :
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
+ * - MEDIA_BUS_FMT_YUV16_1X48,
+ * - MEDIA_BUS_FMT_RGB161616_1X48,
+ * - MEDIA_BUS_FMT_UYVY12_1X24,
+ * - MEDIA_BUS_FMT_YUV12_1X36,
+ * - MEDIA_BUS_FMT_RGB121212_1X36,
+ * - MEDIA_BUS_FMT_UYVY10_1X20,
+ * - MEDIA_BUS_FMT_YUV10_1X30,
+ * - MEDIA_BUS_FMT_RGB101010_1X30,
+ * - MEDIA_BUS_FMT_UYVY8_1X16,
+ * - MEDIA_BUS_FMT_YUV8_1X24,
+ * - MEDIA_BUS_FMT_RGB888_1X24,
+ */
+
+/* Can return a maximum of 11 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 11
+
+static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ struct drm_connector *conn = conn_state->connector;
+ struct drm_display_info *info = &conn->display_info;
+ struct drm_display_mode *mode = &crtc_state->mode;
+ u8 max_bpc = conn_state->max_requested_bpc;
+ bool is_hdmi2_sink = info->hdmi.scdc.supported ||
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
+ u32 *output_fmts;
+ unsigned int i = 0;
+
+ *num_output_fmts = 0;
+
+ output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
+ GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+
+ /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */
+ if (list_is_singular(&bridge->encoder->bridge_chain) ||
+ list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) {
+ *num_output_fmts = 1;
+ output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+
+ return output_fmts;
+ }
+
+ /*
+ * If the current mode enforces 4:2:0, force the output but format
+ * to 4:2:0 and do not add the YUV422/444/RGB formats
+ */
+ if (conn->ycbcr_420_allowed &&
+ (drm_mode_is_420_only(info, mode) ||
+ (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
+
+ /* Order bus formats from 16bit to 8bit if supported */
+ if (max_bpc >= 16 && info->bpc == 16 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
+
+ if (max_bpc >= 12 && info->bpc >= 12 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
+
+ if (max_bpc >= 10 && info->bpc >= 10 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+
+ /* Default 8bit fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+ }
+
+ /*
+ * Order bus formats from 16bit to 8bit and from YUV422 to RGB
+ * if supported. In any case the default RGB888 format is added
+ */
+
+ /* Default 8bit RGB fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+
+ if (max_bpc >= 16 && info->bpc == 16) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ }
+
+ if (max_bpc >= 12 && info->bpc >= 12) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ }
+
+ if (max_bpc >= 10 && info->bpc >= 10) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ }
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+}
+
+/*
+ * Possible input formats :
+ * - MEDIA_BUS_FMT_RGB888_1X24
+ * - MEDIA_BUS_FMT_YUV8_1X24
+ * - MEDIA_BUS_FMT_UYVY8_1X16
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
+ * - MEDIA_BUS_FMT_RGB101010_1X30
+ * - MEDIA_BUS_FMT_YUV10_1X30
+ * - MEDIA_BUS_FMT_UYVY10_1X20
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
+ * - MEDIA_BUS_FMT_RGB121212_1X36
+ * - MEDIA_BUS_FMT_YUV12_1X36
+ * - MEDIA_BUS_FMT_UYVY12_1X24
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
+ * - MEDIA_BUS_FMT_RGB161616_1X48
+ * - MEDIA_BUS_FMT_YUV16_1X48
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
+ */
+
+/* Can return a maximum of 3 possible input formats for an output format */
+#define MAX_INPUT_SEL_FORMATS 3
+
+static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts;
+ unsigned int i = 0;
+
+ *num_input_fmts = 0;
+
+ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ switch (output_fmt) {
+ /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */
+ case MEDIA_BUS_FMT_FIXED:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ /* 8bit */
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+
+ /* 10bit */
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+
+ /* 12bit */
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ break;
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+
+ /* 16bit */
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ break;
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ break;
+
+ /*YUV 4:2:0 */
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ input_fmts[i++] = output_fmt;
+ break;
+ }
+
+ *num_input_fmts = i;
+
+ if (*num_input_fmts == 0) {
+ kfree(input_fmts);
+ input_fmts = NULL;
+ }
+
+ return input_fmts;
+}
+
+static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+ void *data = hdmi->plat_data->phy_data;
+
+ if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) {
+ if (hdmi->plat_data->get_output_bus_format)
+ hdmi->hdmi_data.enc_out_bus_format =
+ hdmi->plat_data->get_output_bus_format(data);
+ else
+ hdmi->hdmi_data.enc_out_bus_format =
+ MEDIA_BUS_FMT_RGB888_1X24;
+
+ if (hdmi->plat_data->get_input_bus_format)
+ hdmi->hdmi_data.enc_in_bus_format =
+ hdmi->plat_data->get_input_bus_format(data);
+ else if (hdmi->plat_data->input_bus_format)
+ hdmi->hdmi_data.enc_in_bus_format =
+ hdmi->plat_data->input_bus_format;
+ else
+ hdmi->hdmi_data.enc_in_bus_format =
+ MEDIA_BUS_FMT_RGB888_1X24;
+ } else {
+ hdmi->hdmi_data.enc_out_bus_format =
+ bridge_state->output_bus_cfg.format;
+
+ hdmi->hdmi_data.enc_in_bus_format =
+ bridge_state->input_bus_cfg.format;
+
+ dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
+ bridge_state->input_bus_cfg.format,
+ bridge_state->output_bus_cfg.format);
+ }
+
+ return 0;
+}
+
+static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+ int ret;
+
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return 0;
+
+ if (hdmi->next_bridge) {
+ hdmi->next_bridge->encoder = bridge->encoder;
+ ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags);
+ if (ret) {
+ DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return dw_hdmi_connector_create(hdmi);
+}
+
+static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ cec_notifier_conn_unregister(hdmi->cec_notifier);
+ hdmi->cec_notifier = NULL;
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+}
+
static enum drm_mode_status
dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct dw_hdmi *hdmi = bridge->driver_private;
- struct drm_connector *connector = &hdmi->connector;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
if (hdmi->next_bridge)
@@ -3498,15 +3959,16 @@
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);
+ if (pdata->mode_valid)
+ mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
+ mode);
return mode_status;
}
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
- struct drm_display_mode *orig_mode,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *orig_mode,
+ const struct drm_display_mode *mode)
{
struct dw_hdmi *hdmi = bridge->driver_private;
@@ -3518,32 +3980,35 @@
mutex_unlock(&hdmi->mutex);
}
-static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
+static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
{
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);
+ hdmi->curr_conn = NULL;
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)
+static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ struct drm_atomic_state *state = old_state->base.state;
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
mutex_lock(&hdmi->mutex);
hdmi->disabled = false;
+ hdmi->curr_conn = connector;
if (hdmi->plat_data->dclk_set)
hdmi->plat_data->dclk_set(hdmi->plat_data->phy_data, true, 0);
dw_hdmi_update_power(hdmi);
@@ -3552,13 +4017,47 @@
mutex_unlock(&hdmi->mutex);
}
+static enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_detect(hdmi);
+}
+
+static struct edid *dw_hdmi_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_get_edid(hdmi, connector);
+}
+
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
.attach = dw_hdmi_bridge_attach,
- .enable = dw_hdmi_bridge_enable,
- .disable = dw_hdmi_bridge_disable,
+ .detach = dw_hdmi_bridge_detach,
+ .atomic_check = dw_hdmi_bridge_atomic_check,
+ .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
+ .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
+ .atomic_enable = dw_hdmi_bridge_atomic_enable,
+ .atomic_disable = dw_hdmi_bridge_atomic_disable,
.mode_set = dw_hdmi_bridge_mode_set,
.mode_valid = dw_hdmi_bridge_mode_valid,
+ .detect = dw_hdmi_bridge_detect,
+ .get_edid = dw_hdmi_bridge_get_edid,
};
+
+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap)
+{
+ hdmi->cec_adap = adap;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap);
+
+/* -----------------------------------------------------------------------------
+ * IRQ Handling
+ */
static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
{
@@ -3668,9 +4167,11 @@
phy_stat & HDMI_PHY_HPD,
phy_stat & HDMI_PHY_RX_SENSE);
- if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
- cec_notifier_set_phys_addr(hdmi->cec_notifier,
- CEC_PHYS_ADDR_INVALID);
+ if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) {
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+ }
}
check_hdmi_irq(hdmi, intr_stat, phy_int_pol);
@@ -3813,6 +4314,21 @@
.max_register = HDMI_I2CM_SCDC_UPDATE1 << 2,
};
+static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
+{
+ initialize_hdmi_ih_mutes(hdmi);
+
+ /*
+ * Reset HDMI DDC I2C master controller and mute I2CM interrupts.
+ * Even if we are using a separate i2c adapter doing this doesn't
+ * hurt.
+ */
+ dw_hdmi_i2c_init(hdmi);
+
+ if (hdmi->phy.ops->setup_hpd)
+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
+}
+
static int dw_hdmi_status_show(struct seq_file *s, void *v)
{
struct dw_hdmi *hdmi = s->private;
@@ -3871,22 +4387,22 @@
}
val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN);
- if (!(val & HDMI_FC_PACKET_DRM_TX_EN_MASK)) {
+ if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) {
seq_puts(s, "Off\n");
return 0;
}
switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) {
- case TRADITIONAL_GAMMA_SDR:
+ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR:
seq_puts(s, "SDR");
break;
- case TRADITIONAL_GAMMA_HDR:
+ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR:
seq_puts(s, "HDR");
break;
- case SMPTE_ST2084:
+ case HDMI_EOTF_SMPTE_ST2084:
seq_puts(s, "ST2084");
break;
- case HLG:
+ case HDMI_EOTF_BT_2100_HLG:
seq_puts(s, "HLG");
break;
default:
@@ -4163,9 +4679,22 @@
return 0;
}
-static struct dw_hdmi *
-__dw_hdmi_probe(struct platform_device *pdev,
- const struct dw_hdmi_plat_data *plat_data)
+void
+dw_hdmi_cec_wake_ops_register(struct dw_hdmi *hdmi, const struct dw_hdmi_cec_wake_ops *cec_ops)
+{
+ if (!cec_ops || !hdmi)
+ return;
+
+ hdmi->cec_ops = cec_ops;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_cec_wake_ops_register);
+
+
+/* -----------------------------------------------------------------------------
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
+ const struct dw_hdmi_plat_data *plat_data)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@@ -4191,7 +4720,6 @@
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;
@@ -4201,6 +4729,7 @@
mutex_init(&hdmi->mutex);
mutex_init(&hdmi->audio_mutex);
+ mutex_init(&hdmi->cec_notifier_mutex);
spin_lock_init(&hdmi->audio_lock);
ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
@@ -4344,7 +4873,6 @@
}
init_hpd_work(hdmi);
- initialize_hdmi_ih_mutes(hdmi);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -4359,12 +4887,6 @@
if (ret)
goto err_iahb;
- hdmi->cec_notifier = cec_notifier_get(dev);
- if (!hdmi->cec_notifier) {
- ret = -ENOMEM;
- goto err_iahb;
- }
-
/*
* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
* N and cts values before enabling phy
@@ -4373,6 +4895,24 @@
/* If DDC bus is not specified, try to register HDMI I2C bus */
if (!hdmi->ddc) {
+ /* Look for (optional) stuff related to unwedging */
+ hdmi->pinctrl = devm_pinctrl_get(dev);
+ if (!IS_ERR(hdmi->pinctrl)) {
+ hdmi->unwedge_state =
+ pinctrl_lookup_state(hdmi->pinctrl, "unwedge");
+ hdmi->default_state =
+ pinctrl_lookup_state(hdmi->pinctrl, "default");
+
+ if (IS_ERR(hdmi->default_state) ||
+ IS_ERR(hdmi->unwedge_state)) {
+ if (!IS_ERR(hdmi->unwedge_state))
+ dev_warn(dev,
+ "Unwedge requires default pinctrl\n");
+ hdmi->default_state = NULL;
+ hdmi->unwedge_state = NULL;
+ }
+ }
+
hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->ddc))
hdmi->ddc = NULL;
@@ -4388,8 +4928,12 @@
hdmi->i2c->scl_low_ns = 4916;
}
+ dw_hdmi_init_hw(hdmi);
+
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_HPD;
#ifdef CONFIG_OF
hdmi->bridge.of_node = pdev->dev.of_node;
#endif
@@ -4418,16 +4962,6 @@
hdmi->sink_has_audio = true;
}
- dw_hdmi_setup_i2c(hdmi);
- if (hdmi->phy.ops->setup_hpd)
- hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
-
- if (hdmi->version >= 0x200a)
- hdmi->connector.ycbcr_420_allowed =
- hdmi->plat_data->ycbcr_420_allowed;
- else
- hdmi->connector.ycbcr_420_allowed = false;
-
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
@@ -4442,7 +4976,7 @@
audio.base = hdmi->regs;
audio.irq = irq;
audio.hdmi = hdmi;
- audio.eld = hdmi->connector.eld;
+ audio.get_eld = hdmi_audio_get_eld;
hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
@@ -4455,6 +4989,7 @@
struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi;
+ audio.get_eld = hdmi_audio_get_eld;
audio.write = hdmi_writeb;
audio.read = hdmi_readb;
audio.mod = hdmi_modb;
@@ -4510,9 +5045,7 @@
goto err_iahb;
}
- /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */
- if (hdmi->i2c)
- dw_hdmi_i2c_init(hdmi);
+ drm_bridge_add(&hdmi->bridge);
dw_hdmi_register_debugfs(dev, hdmi);
@@ -4526,26 +5059,22 @@
return hdmi;
err_iahb:
- if (hdmi->i2c) {
- i2c_del_adapter(&hdmi->i2c->adap);
- hdmi->ddc = NULL;
- }
-
- if (hdmi->cec_notifier)
- cec_notifier_put(hdmi->cec_notifier);
-
clk_disable_unprepare(hdmi->iahb_clk);
if (hdmi->cec_clk)
clk_disable_unprepare(hdmi->cec_clk);
err_isfr:
clk_disable_unprepare(hdmi->isfr_clk);
err_res:
- i2c_put_adapter(hdmi->ddc);
+ if (hdmi->i2c)
+ i2c_del_adapter(&hdmi->i2c->adap);
+ else
+ i2c_put_adapter(hdmi->ddc);
return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_probe);
-static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
+void dw_hdmi_remove(struct dw_hdmi *hdmi)
{
if (hdmi->irq)
disable_irq(hdmi->irq);
@@ -4555,6 +5084,8 @@
destroy_workqueue(hdmi->workqueue);
debugfs_remove_recursive(hdmi->debugfs_dir);
+
+ drm_bridge_remove(&hdmi->bridge);
if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio);
@@ -4574,9 +5105,6 @@
if (hdmi->bridge.encoder)
hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
- if (hdmi->cec_notifier)
- cec_notifier_put(hdmi->cec_notifier);
-
clk_disable_unprepare(hdmi->iahb_clk);
clk_disable_unprepare(hdmi->isfr_clk);
if (hdmi->cec_clk)
@@ -4586,31 +5114,6 @@
i2c_del_adapter(&hdmi->i2c->adap);
else
i2c_put_adapter(hdmi->ddc);
-}
-
-/* -----------------------------------------------------------------------------
- * Probe/remove API, used from platforms based on the DRM bridge API.
- */
-struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
- const struct dw_hdmi_plat_data *plat_data)
-{
- struct dw_hdmi *hdmi;
-
- hdmi = __dw_hdmi_probe(pdev, plat_data);
- if (IS_ERR(hdmi))
- return hdmi;
-
- drm_bridge_add(&hdmi->bridge);
-
- return hdmi;
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_probe);
-
-void dw_hdmi_remove(struct dw_hdmi *hdmi)
-{
- drm_bridge_remove(&hdmi->bridge);
-
- __dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_remove);
@@ -4624,13 +5127,13 @@
struct dw_hdmi *hdmi;
int ret;
- hdmi = __dw_hdmi_probe(pdev, plat_data);
+ hdmi = dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
return hdmi;
- ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
if (ret) {
- __dw_hdmi_remove(hdmi);
+ dw_hdmi_remove(hdmi);
DRM_ERROR("Failed to initialize bridge with drm\n");
return ERR_PTR(ret);
}
@@ -4644,7 +5147,7 @@
void dw_hdmi_unbind(struct dw_hdmi *hdmi)
{
- __dw_hdmi_remove(hdmi);
+ dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
@@ -4673,12 +5176,10 @@
}
}
-void dw_hdmi_suspend(struct device *dev, struct dw_hdmi *hdmi)
+void dw_hdmi_suspend(struct dw_hdmi *hdmi)
{
- if (!hdmi) {
- dev_warn(dev, "Hdmi has not been initialized\n");
+ if (!hdmi)
return;
- }
mutex_lock(&hdmi->mutex);
@@ -4699,22 +5200,19 @@
disable_irq(hdmi->irq);
cancel_delayed_work(&hdmi->work);
flush_workqueue(hdmi->workqueue);
- pinctrl_pm_select_sleep_state(dev);
+ pinctrl_pm_select_sleep_state(hdmi->dev);
}
EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
-void dw_hdmi_resume(struct device *dev, struct dw_hdmi *hdmi)
+void dw_hdmi_resume(struct dw_hdmi *hdmi)
{
- if (!hdmi) {
- dev_warn(dev, "Hdmi has not been initialized\n");
+ if (!hdmi)
return;
- }
- pinctrl_pm_select_default_state(dev);
+ pinctrl_pm_select_default_state(hdmi->dev);
mutex_lock(&hdmi->mutex);
dw_hdmi_reg_initial(hdmi);
- if (hdmi->i2c)
- dw_hdmi_i2c_init(hdmi);
+ dw_hdmi_i2c_init(hdmi);
if (hdmi->irq)
enable_irq(hdmi->irq);
/*
--
Gitblit v1.6.2