mirror of
https://github.com/Fishwaldo/build.git
synced 2025-07-22 04:48:37 +00:00
* Introducing Rockchip rk322x SoC support Main features: - Legacy kernel flavour based upon stable v2.x rk3288 Rockchip branch (https://github.com/rockchip-linux/kernel/tree/stable-4.4-rk3288-linux-v2.x) - Current kernel flavour based on mainline 5.6.y kernel - Mainline u-boot (v2020.04) - Single generic tv box target (rk322x-box) which boots on all the known tv boxes - Hardware devices (eMMC/NAND, led wiring configuration, SoC variant selection) modulation done by user at runtime via device tree overlays - a script (rk322x-config) is provided for autodetection and simple configuration by inexperienced users; - Bits added to armbian-hardware-optimization to set affinity for irq handlers - rk322x-box targets already added to targets.conf for automatic image creation * Removed disabled patches * Restored mysteriously removed comment character
2372 lines
80 KiB
Diff
2372 lines
80 KiB
Diff
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
|
|
index aaa1bf81d00d..c903112fc0c1 100644
|
|
--- a/drivers/media/rc/keymaps/Makefile
|
|
+++ b/drivers/media/rc/keymaps/Makefile
|
|
@@ -79,6 +79,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
|
|
rc-npgtech.o \
|
|
rc-odroid.o \
|
|
rc-pctv-sedna.o \
|
|
+ rc-pine64.o \
|
|
rc-pinnacle-color.o \
|
|
rc-pinnacle-grey.o \
|
|
rc-pinnacle-pctv-hd.o \
|
|
diff --git a/drivers/media/rc/keymaps/rc-pine64.c b/drivers/media/rc/keymaps/rc-pine64.c
|
|
new file mode 100644
|
|
index 000000000000..94e5624f63f4
|
|
--- /dev/null
|
|
+++ b/drivers/media/rc/keymaps/rc-pine64.c
|
|
@@ -0,0 +1,59 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+// Keytable for Pine64 IR Remote Controller
|
|
+// Copyright (c) 2017 Jonas Karlman
|
|
+
|
|
+#include <media/rc-map.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+static struct rc_map_table pine64[] = {
|
|
+ { 0x404000, KEY_NUMERIC_0 },
|
|
+ { 0x404001, KEY_NUMERIC_1 },
|
|
+ { 0x404002, KEY_NUMERIC_2 },
|
|
+ { 0x404003, KEY_NUMERIC_3 },
|
|
+ { 0x404004, KEY_NUMERIC_4 },
|
|
+ { 0x404005, KEY_NUMERIC_5 },
|
|
+ { 0x404006, KEY_NUMERIC_6 },
|
|
+ { 0x404007, KEY_NUMERIC_7 },
|
|
+ { 0x404008, KEY_NUMERIC_8 },
|
|
+ { 0x404009, KEY_NUMERIC_9 },
|
|
+ { 0x40400a, KEY_MUTE },
|
|
+ { 0x40400b, KEY_UP },
|
|
+ { 0x40400c, KEY_BACKSPACE },
|
|
+ { 0x40400d, KEY_OK },
|
|
+ { 0x40400e, KEY_DOWN },
|
|
+ { 0x404010, KEY_LEFT },
|
|
+ { 0x404011, KEY_RIGHT },
|
|
+ { 0x404017, KEY_VOLUMEDOWN },
|
|
+ { 0x404018, KEY_VOLUMEUP },
|
|
+ { 0x40401a, KEY_HOME },
|
|
+ { 0x40401d, KEY_MENU },
|
|
+ { 0x40401f, KEY_WWW },
|
|
+ { 0x404045, KEY_BACK },
|
|
+ { 0x404047, KEY_CONTEXT_MENU },
|
|
+ { 0x40404d, KEY_POWER },
|
|
+};
|
|
+
|
|
+static struct rc_map_list pine64_map = {
|
|
+ .map = {
|
|
+ .scan = pine64,
|
|
+ .size = ARRAY_SIZE(pine64),
|
|
+ .rc_proto = RC_PROTO_NECX,
|
|
+ .name = RC_MAP_PINE64,
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init init_rc_map_pine64(void)
|
|
+{
|
|
+ return rc_map_register(&pine64_map);
|
|
+}
|
|
+
|
|
+static void __exit exit_rc_map_pine64(void)
|
|
+{
|
|
+ rc_map_unregister(&pine64_map);
|
|
+}
|
|
+
|
|
+module_init(init_rc_map_pine64)
|
|
+module_exit(exit_rc_map_pine64)
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Jonas Karlman");
|
|
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
|
|
index d22810dcd85c..bd1f4f4347d5 100644
|
|
--- a/include/media/rc-map.h
|
|
+++ b/include/media/rc-map.h
|
|
@@ -233,6 +233,7 @@ struct rc_map *rc_map_get(const char *name);
|
|
#define RC_MAP_NPGTECH "rc-npgtech"
|
|
#define RC_MAP_ODROID "rc-odroid"
|
|
#define RC_MAP_PCTV_SEDNA "rc-pctv-sedna"
|
|
+#define RC_MAP_PINE64 "rc-pine64"
|
|
#define RC_MAP_PINNACLE_COLOR "rc-pinnacle-color"
|
|
#define RC_MAP_PINNACLE_GREY "rc-pinnacle-grey"
|
|
#define RC_MAP_PINNACLE_PCTV_HD "rc-pinnacle-pctv-hd"
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 64be34f37bf89d457bc4352f8f8c663a30464a40 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Wed, 7 Aug 2019 15:11:23 +0000
|
|
Subject: [PATCH] ASoC: hdmi-codec: reorder channel allocation list
|
|
|
|
Wrong channel allocation is selected by hdmi_codec_get_ch_alloc_table_idx().
|
|
|
|
E.g when ELD reports FL|FR|LFE|FC|RL|RR or FL|FR|LFE|FC|RL|RR|RC|RLC|RRC
|
|
|
|
ca_id 0x01 with speaker mask FL|FR|LFE gets selected instead of
|
|
ca_id 0x03 with speaker mask FL|FR|LFE|FC for 4 channels
|
|
|
|
and
|
|
|
|
ca_id 0x04 with speaker mask FL|FR|RC gets selected instead of
|
|
ca_id 0x0b with speaker mask FL|FR|LFE|FC|RL|RR for 6 channels
|
|
|
|
Fix this by reorder the channel allocation list with
|
|
most specific speaker mask at the top.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
sound/soc/codecs/hdmi-codec.c | 115 ++++++++++++++++------------------
|
|
1 file changed, 53 insertions(+), 62 deletions(-)
|
|
|
|
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
|
|
index f005751da2cc..437759e5f459 100644
|
|
--- a/sound/soc/codecs/hdmi-codec.c
|
|
+++ b/sound/soc/codecs/hdmi-codec.c
|
|
@@ -189,84 +189,75 @@ static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
|
|
/*
|
|
* hdmi_codec_channel_alloc: speaker configuration available for CEA
|
|
*
|
|
- * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
|
|
+ * This is an ordered list where ca_id must exist in hdmi_codec_8ch_chmaps
|
|
* The preceding ones have better chances to be selected by
|
|
* hdmi_codec_get_ch_alloc_table_idx().
|
|
*/
|
|
static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
|
|
{ .ca_id = 0x00, .n_ch = 2,
|
|
- .mask = FL | FR},
|
|
- /* 2.1 */
|
|
- { .ca_id = 0x01, .n_ch = 4,
|
|
- .mask = FL | FR | LFE},
|
|
- /* Dolby Surround */
|
|
+ .mask = FL | FR },
|
|
+ { .ca_id = 0x03, .n_ch = 4,
|
|
+ .mask = FL | FR | LFE | FC },
|
|
{ .ca_id = 0x02, .n_ch = 4,
|
|
.mask = FL | FR | FC },
|
|
- /* surround51 */
|
|
+ { .ca_id = 0x01, .n_ch = 4,
|
|
+ .mask = FL | FR | LFE },
|
|
{ .ca_id = 0x0b, .n_ch = 6,
|
|
- .mask = FL | FR | LFE | FC | RL | RR},
|
|
- /* surround40 */
|
|
- { .ca_id = 0x08, .n_ch = 6,
|
|
- .mask = FL | FR | RL | RR },
|
|
- /* surround41 */
|
|
- { .ca_id = 0x09, .n_ch = 6,
|
|
- .mask = FL | FR | LFE | RL | RR },
|
|
- /* surround50 */
|
|
+ .mask = FL | FR | LFE | FC | RL | RR },
|
|
{ .ca_id = 0x0a, .n_ch = 6,
|
|
.mask = FL | FR | FC | RL | RR },
|
|
- /* 6.1 */
|
|
- { .ca_id = 0x0f, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FC | RL | RR | RC },
|
|
- /* surround71 */
|
|
+ { .ca_id = 0x09, .n_ch = 6,
|
|
+ .mask = FL | FR | LFE | RL | RR },
|
|
+ { .ca_id = 0x08, .n_ch = 6,
|
|
+ .mask = FL | FR | RL | RR },
|
|
+ { .ca_id = 0x07, .n_ch = 6,
|
|
+ .mask = FL | FR | LFE | FC | RC },
|
|
+ { .ca_id = 0x06, .n_ch = 6,
|
|
+ .mask = FL | FR | FC | RC },
|
|
+ { .ca_id = 0x05, .n_ch = 6,
|
|
+ .mask = FL | FR | LFE | RC },
|
|
+ { .ca_id = 0x04, .n_ch = 6,
|
|
+ .mask = FL | FR | RC },
|
|
{ .ca_id = 0x13, .n_ch = 8,
|
|
.mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
|
|
- /* others */
|
|
- { .ca_id = 0x03, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FC },
|
|
- { .ca_id = 0x04, .n_ch = 8,
|
|
- .mask = FL | FR | RC},
|
|
- { .ca_id = 0x05, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RC },
|
|
- { .ca_id = 0x06, .n_ch = 8,
|
|
- .mask = FL | FR | FC | RC },
|
|
- { .ca_id = 0x07, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FC | RC },
|
|
- { .ca_id = 0x0c, .n_ch = 8,
|
|
- .mask = FL | FR | RC | RL | RR },
|
|
- { .ca_id = 0x0d, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RL | RR | RC },
|
|
- { .ca_id = 0x0e, .n_ch = 8,
|
|
- .mask = FL | FR | FC | RL | RR | RC },
|
|
- { .ca_id = 0x10, .n_ch = 8,
|
|
- .mask = FL | FR | RL | RR | RLC | RRC },
|
|
- { .ca_id = 0x11, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RL | RR | RLC | RRC },
|
|
+ { .ca_id = 0x1f, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
|
|
{ .ca_id = 0x12, .n_ch = 8,
|
|
.mask = FL | FR | FC | RL | RR | RLC | RRC },
|
|
- { .ca_id = 0x14, .n_ch = 8,
|
|
- .mask = FL | FR | FLC | FRC },
|
|
- { .ca_id = 0x15, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FLC | FRC },
|
|
- { .ca_id = 0x16, .n_ch = 8,
|
|
- .mask = FL | FR | FC | FLC | FRC },
|
|
- { .ca_id = 0x17, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FC | FLC | FRC },
|
|
- { .ca_id = 0x18, .n_ch = 8,
|
|
- .mask = FL | FR | RC | FLC | FRC },
|
|
- { .ca_id = 0x19, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RC | FLC | FRC },
|
|
- { .ca_id = 0x1a, .n_ch = 8,
|
|
- .mask = FL | FR | RC | FC | FLC | FRC },
|
|
- { .ca_id = 0x1b, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RC | FC | FLC | FRC },
|
|
- { .ca_id = 0x1c, .n_ch = 8,
|
|
- .mask = FL | FR | RL | RR | FLC | FRC },
|
|
- { .ca_id = 0x1d, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | RL | RR | FLC | FRC },
|
|
{ .ca_id = 0x1e, .n_ch = 8,
|
|
.mask = FL | FR | FC | RL | RR | FLC | FRC },
|
|
- { .ca_id = 0x1f, .n_ch = 8,
|
|
- .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
|
|
+ { .ca_id = 0x11, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | RL | RR | RLC | RRC },
|
|
+ { .ca_id = 0x1d, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | RL | RR | FLC | FRC },
|
|
+ { .ca_id = 0x10, .n_ch = 8,
|
|
+ .mask = FL | FR | RL | RR | RLC | RRC },
|
|
+ { .ca_id = 0x1c, .n_ch = 8,
|
|
+ .mask = FL | FR | RL | RR | FLC | FRC },
|
|
+ { .ca_id = 0x0f, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | FC | RL | RR | RC },
|
|
+ { .ca_id = 0x1b, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | RC | FC | FLC | FRC },
|
|
+ { .ca_id = 0x0e, .n_ch = 8,
|
|
+ .mask = FL | FR | FC | RL | RR | RC },
|
|
+ { .ca_id = 0x1a, .n_ch = 8,
|
|
+ .mask = FL | FR | RC | FC | FLC | FRC },
|
|
+ { .ca_id = 0x0d, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | RL | RR | RC },
|
|
+ { .ca_id = 0x19, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | RC | FLC | FRC },
|
|
+ { .ca_id = 0x0c, .n_ch = 8,
|
|
+ .mask = FL | FR | RC | RL | RR },
|
|
+ { .ca_id = 0x18, .n_ch = 8,
|
|
+ .mask = FL | FR | RC | FLC | FRC },
|
|
+ { .ca_id = 0x17, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | FC | FLC | FRC },
|
|
+ { .ca_id = 0x16, .n_ch = 8,
|
|
+ .mask = FL | FR | FC | FLC | FRC },
|
|
+ { .ca_id = 0x15, .n_ch = 8,
|
|
+ .mask = FL | FR | LFE | FLC | FRC },
|
|
+ { .ca_id = 0x14, .n_ch = 8,
|
|
+ .mask = FL | FR | FLC | FRC },
|
|
};
|
|
|
|
struct hdmi_codec_priv {
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 5e626ea314cb0b25dc7a58e72908b1f91e762dfc Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Tue, 1 Oct 2019 20:52:42 +0000
|
|
Subject: [PATCH] media: cec-adap: add debounce support when setting an invalid
|
|
phys addr
|
|
|
|
When EDID is refreshed, HDMI cable is unplugged/replugged or
|
|
an AVR is power cycled the CEC phys addr gets invalidated.
|
|
|
|
This can cause some disruption of CEC communication when
|
|
adapter is being reconfigured.
|
|
|
|
Add a debounce module option that can be used to debounce setting
|
|
an invalid phys addr. Default is not to use debouncing.
|
|
|
|
Using a configured debounce of e.g. 5000 ms, cec reconfiguring
|
|
could be avoided when AVR was power cycled on my setup.
|
|
|
|
Power off AVR (default cec.debounce=0):
|
|
[ 101.536866] cec-dw_hdmi: new physical address f.f.f.f
|
|
[ 102.495686] cec-dw_hdmi: new physical address 2.1.0.0
|
|
[ 102.495913] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
|
|
[ 102.628574] cec-dw_hdmi: config: la 1 pa 2.1.0.0
|
|
[ 105.130115] cec-dw_hdmi: new physical address f.f.f.f
|
|
[ 106.979705] cec-dw_hdmi: new physical address 2.1.0.0
|
|
[ 106.979872] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
|
|
[ 107.112399] cec-dw_hdmi: config: la 1 pa 2.1.0.0
|
|
[ 108.979408] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
|
|
[ 109.205386] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11
|
|
|
|
Power on AVR (default cec.debounce=0):
|
|
[ 158.398447] cec-dw_hdmi: new physical address f.f.f.f
|
|
[ 161.977714] cec-dw_hdmi: new physical address 2.1.0.0
|
|
[ 161.978766] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
|
|
[ 162.115624] cec-dw_hdmi: config: la 1 pa 2.1.0.0
|
|
[ 162.402750] cec-dw_hdmi: new physical address f.f.f.f
|
|
[ 162.403389] cec-dw_hdmi: cec_transmit_msg_fh: adapter is unconfigured
|
|
[ 162.886757] cec-dw_hdmi: new physical address 2.1.0.0
|
|
[ 162.886964] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
|
|
[ 163.510725] cec-dw_hdmi: config: la 1 pa 2.1.0.0
|
|
[ 173.034200] cec-dw_hdmi: message 10 89 02 05 timed out
|
|
|
|
Power off AVR (cec.debounce=5000):
|
|
[ 251.720471] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
|
|
[ 251.922432] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11
|
|
|
|
Power on AVR (cec.debounce=5000):
|
|
[ 291.154262] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
|
|
[ 291.296199] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
Documentation/media/uapi/cec/cec-intro.rst | 8 ++++++++
|
|
drivers/media/cec/cec-adap.c | 9 ++++++++-
|
|
drivers/media/cec/cec-core.c | 18 ++++++++++++++++++
|
|
drivers/media/cec/cec-priv.h | 1 +
|
|
include/media/cec.h | 2 ++
|
|
5 files changed, 37 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/media/uapi/cec/cec-intro.rst b/Documentation/media/uapi/cec/cec-intro.rst
|
|
index 05088fcefe81..9bfd11ef987b 100644
|
|
--- a/Documentation/media/uapi/cec/cec-intro.rst
|
|
+++ b/Documentation/media/uapi/cec/cec-intro.rst
|
|
@@ -47,3 +47,11 @@ provides three tools to handle CEC:
|
|
determine how compliant the CEC implementation is.
|
|
|
|
- cec-follower: emulates a CEC follower.
|
|
+
|
|
+Debouncing
|
|
+----------
|
|
+
|
|
+The ``debounce_ms`` option is a module parameter that can be used to enabled
|
|
+debouncing of setting invalid physical address.
|
|
+
|
|
+FIXME: Make a section "1.1 Debouncing" that explains this module option.
|
|
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
|
|
index 6c95dc471d4c..c483e847db95 100644
|
|
--- a/drivers/media/cec/cec-adap.c
|
|
+++ b/drivers/media/cec/cec-adap.c
|
|
@@ -1609,8 +1609,15 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
|
|
if (IS_ERR_OR_NULL(adap))
|
|
return;
|
|
|
|
+ cancel_delayed_work_sync(&adap->debounce_work);
|
|
+
|
|
mutex_lock(&adap->lock);
|
|
- __cec_s_phys_addr(adap, phys_addr, block);
|
|
+ if (cec_debounce_ms > 0 && !block &&
|
|
+ phys_addr == CEC_PHYS_ADDR_INVALID && adap->phys_addr != phys_addr)
|
|
+ schedule_delayed_work(&adap->debounce_work,
|
|
+ msecs_to_jiffies(cec_debounce_ms));
|
|
+ else
|
|
+ __cec_s_phys_addr(adap, phys_addr, block);
|
|
mutex_unlock(&adap->lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(cec_s_phys_addr);
|
|
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
|
|
index 0c52e1bb3910..cd71d77c057c 100644
|
|
--- a/drivers/media/cec/cec-core.c
|
|
+++ b/drivers/media/cec/cec-core.c
|
|
@@ -28,6 +28,10 @@ static bool debug_phys_addr;
|
|
module_param(debug_phys_addr, bool, 0644);
|
|
MODULE_PARM_DESC(debug_phys_addr, "add CEC_CAP_PHYS_ADDR if set");
|
|
|
|
+int cec_debounce_ms;
|
|
+module_param_named(debounce_ms, cec_debounce_ms, int, 0644);
|
|
+MODULE_PARM_DESC(debounce_ms, "invalid physical address debounce time in ms");
|
|
+
|
|
static dev_t cec_dev_t;
|
|
|
|
/* Active devices */
|
|
@@ -174,6 +178,8 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
|
|
devnode->unregistered = true;
|
|
mutex_unlock(&devnode->lock);
|
|
|
|
+ cancel_delayed_work_sync(&adap->debounce_work);
|
|
+
|
|
mutex_lock(&adap->lock);
|
|
__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
|
|
__cec_s_log_addrs(adap, NULL, false);
|
|
@@ -232,6 +238,17 @@ static const struct file_operations cec_error_inj_fops = {
|
|
};
|
|
#endif
|
|
|
|
+static void cec_s_phys_addr_debounce(struct work_struct *work)
|
|
+{
|
|
+ struct delayed_work *delayed_work = to_delayed_work(work);
|
|
+ struct cec_adapter *adap =
|
|
+ container_of(delayed_work, struct cec_adapter, debounce_work);
|
|
+
|
|
+ mutex_lock(&adap->lock);
|
|
+ __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
|
|
+ mutex_unlock(&adap->lock);
|
|
+}
|
|
+
|
|
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
|
void *priv, const char *name, u32 caps,
|
|
u8 available_las)
|
|
@@ -270,6 +287,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
|
INIT_LIST_HEAD(&adap->transmit_queue);
|
|
INIT_LIST_HEAD(&adap->wait_queue);
|
|
init_waitqueue_head(&adap->kthread_waitq);
|
|
+ INIT_DELAYED_WORK(&adap->debounce_work, cec_s_phys_addr_debounce);
|
|
|
|
/* adap->devnode initialization */
|
|
INIT_LIST_HEAD(&adap->devnode.fhs);
|
|
diff --git a/drivers/media/cec/cec-priv.h b/drivers/media/cec/cec-priv.h
|
|
index 9bbd05053d42..d479dbd50528 100644
|
|
--- a/drivers/media/cec/cec-priv.h
|
|
+++ b/drivers/media/cec/cec-priv.h
|
|
@@ -27,6 +27,7 @@ static inline bool msg_is_raw(const struct cec_msg *msg)
|
|
|
|
/* cec-core.c */
|
|
extern int cec_debug;
|
|
+extern int cec_debounce_ms;
|
|
int cec_get_device(struct cec_devnode *devnode);
|
|
void cec_put_device(struct cec_devnode *devnode);
|
|
|
|
diff --git a/include/media/cec.h b/include/media/cec.h
|
|
index 972bc8cd4384..5befad3a6a0f 100644
|
|
--- a/include/media/cec.h
|
|
+++ b/include/media/cec.h
|
|
@@ -164,6 +164,8 @@ struct cec_adapter {
|
|
wait_queue_head_t kthread_waitq;
|
|
wait_queue_head_t waitq;
|
|
|
|
+ struct delayed_work debounce_work;
|
|
+
|
|
const struct cec_adap_ops *ops;
|
|
void *priv;
|
|
u32 capabilities;
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From e7067ef6d71c5686c082f37f8e54454d499124a8 Mon Sep 17 00:00:00 2001
|
|
From: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Date: Thu, 19 Dec 2019 11:11:48 +0100
|
|
Subject: [PATCH] drm/bridge: Add a drm_bridge_state object
|
|
|
|
One of the last remaining objects to not have its atomic state.
|
|
|
|
This is being motivated by our attempt to support runtime bus-format
|
|
negotiation between elements of the bridge chain.
|
|
This patch just paves the road for such a feature by adding a new
|
|
drm_bridge_state object inheriting from drm_private_obj so we can
|
|
re-use some of the existing state initialization/tracking logic.
|
|
|
|
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/drm_atomic.c | 39 +++++++
|
|
drivers/gpu/drm/drm_atomic_helper.c | 20 ++++
|
|
drivers/gpu/drm/drm_bridge.c | 169 +++++++++++++++++++++++++++-
|
|
include/drm/drm_atomic.h | 3 +
|
|
include/drm/drm_bridge.h | 120 ++++++++++++++++++++
|
|
5 files changed, 345 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
|
|
index d33691512a8e..bf1b9c37d515 100644
|
|
--- a/drivers/gpu/drm/drm_atomic.c
|
|
+++ b/drivers/gpu/drm/drm_atomic.c
|
|
@@ -30,6 +30,7 @@
|
|
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_atomic_uapi.h>
|
|
+#include <drm/drm_bridge.h>
|
|
#include <drm/drm_debugfs.h>
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_drv.h>
|
|
@@ -1017,6 +1018,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
|
|
connector->funcs->atomic_print_state(p, state);
|
|
}
|
|
|
|
+/**
|
|
+ * drm_atomic_add_encoder_bridges - add bridges attached to an encoder
|
|
+ * @state: atomic state
|
|
+ * @encoder: DRM encoder
|
|
+ *
|
|
+ * This function adds all bridges attached to @encoder. This is needed to add
|
|
+ * bridge states to @state and make them available when
|
|
+ * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are
|
|
+ * called
|
|
+ *
|
|
+ * Returns:
|
|
+ * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
|
|
+ * then the w/w mutex code has detected a deadlock and the entire atomic
|
|
+ * sequence must be restarted. All other errors are fatal.
|
|
+ */
|
|
+int
|
|
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
|
|
+ struct drm_encoder *encoder)
|
|
+{
|
|
+ struct drm_bridge_state *bridge_state;
|
|
+ struct drm_bridge *bridge;
|
|
+
|
|
+ if (!encoder)
|
|
+ return 0;
|
|
+
|
|
+ DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n",
|
|
+ encoder->base.id, encoder->name, state);
|
|
+
|
|
+ drm_for_each_bridge_in_chain(encoder, bridge) {
|
|
+ bridge_state = drm_atomic_get_bridge_state(state, bridge);
|
|
+ if (IS_ERR(bridge_state))
|
|
+ return PTR_ERR(bridge_state);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_add_encoder_bridges);
|
|
+
|
|
/**
|
|
* drm_atomic_add_affected_connectors - add connectors for CRTC
|
|
* @state: atomic state
|
|
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
|
|
index 4511c2e07bb9..ad8eae98d9e8 100644
|
|
--- a/drivers/gpu/drm/drm_atomic_helper.c
|
|
+++ b/drivers/gpu/drm/drm_atomic_helper.c
|
|
@@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
|
|
return ret;
|
|
}
|
|
|
|
+ /*
|
|
+ * Iterate over all connectors again, and add all affected bridges to
|
|
+ * the state.
|
|
+ */
|
|
+ for_each_oldnew_connector_in_state(state, connector,
|
|
+ old_connector_state,
|
|
+ new_connector_state, i) {
|
|
+ struct drm_encoder *encoder;
|
|
+
|
|
+ encoder = old_connector_state->best_encoder;
|
|
+ ret = drm_atomic_add_encoder_bridges(state, encoder);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ encoder = new_connector_state->best_encoder;
|
|
+ ret = drm_atomic_add_encoder_bridges(state, encoder);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
ret = mode_valid(state);
|
|
if (ret)
|
|
return ret;
|
|
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
|
|
index c2cf0c90fa26..a3921b45f044 100644
|
|
--- a/drivers/gpu/drm/drm_bridge.c
|
|
+++ b/drivers/gpu/drm/drm_bridge.c
|
|
@@ -25,6 +25,7 @@
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
|
|
+#include <drm/drm_atomic_state_helper.h>
|
|
#include <drm/drm_bridge.h>
|
|
#include <drm/drm_encoder.h>
|
|
|
|
@@ -89,6 +90,38 @@ void drm_bridge_remove(struct drm_bridge *bridge)
|
|
}
|
|
EXPORT_SYMBOL(drm_bridge_remove);
|
|
|
|
+static struct drm_private_state *
|
|
+drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj)
|
|
+{
|
|
+ struct drm_bridge *bridge = drm_priv_to_bridge(obj);
|
|
+ struct drm_bridge_state *state;
|
|
+
|
|
+ if (bridge->funcs->atomic_duplicate_state)
|
|
+ state = bridge->funcs->atomic_duplicate_state(bridge);
|
|
+ else
|
|
+ state = drm_atomic_helper_bridge_duplicate_state(bridge);
|
|
+
|
|
+ return state ? &state->base : NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj,
|
|
+ struct drm_private_state *s)
|
|
+{
|
|
+ struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
|
|
+ struct drm_bridge *bridge = drm_priv_to_bridge(obj);
|
|
+
|
|
+ if (bridge->funcs->atomic_destroy_state)
|
|
+ bridge->funcs->atomic_destroy_state(bridge, state);
|
|
+ else
|
|
+ drm_atomic_helper_bridge_destroy_state(bridge, state);
|
|
+}
|
|
+
|
|
+static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
|
|
+ .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
|
|
+ .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
|
|
+};
|
|
+
|
|
/**
|
|
* drm_bridge_attach - attach the bridge to an encoder's chain
|
|
*
|
|
@@ -114,6 +147,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
|
|
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
|
|
struct drm_bridge *previous)
|
|
{
|
|
+ struct drm_bridge_state *state;
|
|
int ret;
|
|
|
|
if (!encoder || !bridge)
|
|
@@ -135,15 +169,35 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
|
|
|
|
if (bridge->funcs->attach) {
|
|
ret = bridge->funcs->attach(bridge);
|
|
- if (ret < 0) {
|
|
- list_del(&bridge->chain_node);
|
|
- bridge->dev = NULL;
|
|
- bridge->encoder = NULL;
|
|
- return ret;
|
|
- }
|
|
+ if (ret < 0)
|
|
+ goto err_reset_bridge;
|
|
+ }
|
|
+
|
|
+ if (bridge->funcs->atomic_reset)
|
|
+ state = bridge->funcs->atomic_reset(bridge);
|
|
+ else
|
|
+ state = drm_atomic_helper_bridge_reset(bridge);
|
|
+
|
|
+ if (IS_ERR(state)) {
|
|
+ ret = PTR_ERR(state);
|
|
+ goto err_detach_bridge;
|
|
}
|
|
|
|
+ drm_atomic_private_obj_init(bridge->dev, &bridge->base,
|
|
+ &state->base,
|
|
+ &drm_bridge_priv_state_funcs);
|
|
+
|
|
return 0;
|
|
+
|
|
+err_detach_bridge:
|
|
+ if (bridge->funcs->detach)
|
|
+ bridge->funcs->detach(bridge);
|
|
+
|
|
+err_reset_bridge:
|
|
+ bridge->dev = NULL;
|
|
+ bridge->encoder = NULL;
|
|
+ list_del(&bridge->chain_node);
|
|
+ return ret;
|
|
}
|
|
EXPORT_SYMBOL(drm_bridge_attach);
|
|
|
|
@@ -155,6 +209,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
|
|
if (WARN_ON(!bridge->dev))
|
|
return;
|
|
|
|
+ drm_atomic_private_obj_fini(&bridge->base);
|
|
+
|
|
if (bridge->funcs->detach)
|
|
bridge->funcs->detach(bridge);
|
|
|
|
@@ -516,6 +572,107 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
|
|
|
|
+/**
|
|
+ * drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
|
|
+ * @bridge: the bridge this state refers to
|
|
+ * @state: state object to destroy
|
|
+ *
|
|
+ * Just a simple kfree() for now.
|
|
+ */
|
|
+void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state)
|
|
+{
|
|
+ kfree(state);
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
|
|
+
|
|
+/**
|
|
+ * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
|
|
+ * default
|
|
+ * @bridge: the bridge this state is refers to
|
|
+ * @state: bridge state to initialize
|
|
+ *
|
|
+ * Initialize the bridge state to default values. This is meant to be* called
|
|
+ * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the
|
|
+ * bridge state.
|
|
+ */
|
|
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state)
|
|
+{
|
|
+ memset(state, 0, sizeof(*state));
|
|
+ state->bridge = bridge;
|
|
+}
|
|
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
|
|
+
|
|
+/**
|
|
+ * drm_atomic_helper_bridge_reset() - default &drm_plane_funcs.reset hook for
|
|
+ * bridges
|
|
+ * @bridge: the bridge to reset state on
|
|
+ *
|
|
+ * Resets the atomic state for @bridge by freeing the state pointer (which
|
|
+ * might be NULL, e.g. at driver load time) and allocating a new empty state
|
|
+ * object.
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * A valid drm_bridge_state object in case of success, an ERR_PTR()
|
|
+ * giving the reaon of the failure otherwise.
|
|
+ */
|
|
+struct drm_bridge_state *
|
|
+drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
|
|
+{
|
|
+ struct drm_bridge_state *bridge_state;
|
|
+
|
|
+ bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
|
|
+ if (!bridge_state)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ __drm_atomic_helper_bridge_reset(bridge, bridge_state);
|
|
+ return bridge_state;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
|
|
+
|
|
+/**
|
|
+ * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state
|
|
+ * @bridge: bridge object
|
|
+ * @state: atomic bridge state
|
|
+ *
|
|
+ * Copies atomic state from a bridge's current state and resets inferred values.
|
|
+ * This is useful for drivers that subclass the bridge state.
|
|
+ */
|
|
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state)
|
|
+{
|
|
+ __drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
|
|
+ &state->base);
|
|
+ state->bridge = bridge;
|
|
+}
|
|
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
|
|
+
|
|
+/**
|
|
+ * drm_atomic_helper_duplicate_bridge_state() - Default duplicate state helper
|
|
+ * @bridge: bridge containing the state to duplicate
|
|
+ *
|
|
+ * Default implementation of &drm_bridge_funcs.atomic_duplicate().
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * a valid state object or NULL if the allocation fails.
|
|
+ */
|
|
+struct drm_bridge_state *
|
|
+drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge)
|
|
+{
|
|
+ struct drm_bridge_state *new;
|
|
+
|
|
+ if (WARN_ON(!bridge->base.state))
|
|
+ return NULL;
|
|
+
|
|
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
|
|
+ if (new)
|
|
+ __drm_atomic_helper_bridge_duplicate_state(bridge, new);
|
|
+
|
|
+ return new;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state);
|
|
+
|
|
#ifdef CONFIG_OF
|
|
/**
|
|
* of_drm_find_bridge - find the bridge corresponding to the device node in
|
|
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
|
|
index 951dfb15c27b..ccce65e14917 100644
|
|
--- a/include/drm/drm_atomic.h
|
|
+++ b/include/drm/drm_atomic.h
|
|
@@ -669,6 +669,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
|
|
return plane->state;
|
|
}
|
|
|
|
+int __must_check
|
|
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
|
|
+ struct drm_encoder *encoder);
|
|
int __must_check
|
|
drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
|
|
struct drm_crtc *crtc);
|
|
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
|
|
index 694e153a7531..8a926c1a08db 100644
|
|
--- a/include/drm/drm_bridge.h
|
|
+++ b/include/drm/drm_bridge.h
|
|
@@ -25,6 +25,8 @@
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/ctype.h>
|
|
+
|
|
+#include <drm/drm_atomic.h>
|
|
#include <drm/drm_encoder.h>
|
|
#include <drm/drm_mode_object.h>
|
|
#include <drm/drm_modes.h>
|
|
@@ -33,6 +35,23 @@ struct drm_bridge;
|
|
struct drm_bridge_timings;
|
|
struct drm_panel;
|
|
|
|
+/**
|
|
+ * struct drm_bridge_state - Atomic bridge state object
|
|
+ * @base: inherit from &drm_private_state
|
|
+ * @bridge: the bridge this state refers to
|
|
+ */
|
|
+struct drm_bridge_state {
|
|
+ struct drm_private_state base;
|
|
+
|
|
+ struct drm_bridge *bridge;
|
|
+};
|
|
+
|
|
+static inline struct drm_bridge_state *
|
|
+drm_priv_to_bridge_state(struct drm_private_state *priv)
|
|
+{
|
|
+ return container_of(priv, struct drm_bridge_state, base);
|
|
+}
|
|
+
|
|
/**
|
|
* struct drm_bridge_funcs - drm_bridge control functions
|
|
*/
|
|
@@ -338,6 +357,49 @@ struct drm_bridge_funcs {
|
|
*/
|
|
void (*atomic_post_disable)(struct drm_bridge *bridge,
|
|
struct drm_atomic_state *old_state);
|
|
+
|
|
+ /**
|
|
+ * @atomic_duplicate_state:
|
|
+ *
|
|
+ * Duplicate the current bridge state object (which is guaranteed to be
|
|
+ * non-NULL).
|
|
+ *
|
|
+ * The atomic_duplicate_state() is optional. When not implemented the
|
|
+ * core allocates a drm_bridge_state object and calls
|
|
+ * &__drm_atomic_helper_bridge_duplicate_state() to initialize it.
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * A valid drm_bridge_state object or NULL if the allocation fails.
|
|
+ */
|
|
+ struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
|
|
+
|
|
+ /**
|
|
+ * @atomic_destroy_state:
|
|
+ *
|
|
+ * Destroy a bridge state object previously allocated by
|
|
+ * &drm_bridge_funcs.atomic_duplicate_state().
|
|
+ *
|
|
+ * The atomic_destroy_state hook is optional. When not implemented the
|
|
+ * core calls kfree() on the state.
|
|
+ */
|
|
+ void (*atomic_destroy_state)(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state);
|
|
+
|
|
+ /**
|
|
+ * @atomic_reset:
|
|
+ *
|
|
+ * Reset the bridge to a predefined state (or retrieve its current
|
|
+ * state) and return a &drm_bridge_state object matching this state.
|
|
+ * This function is called at attach time.
|
|
+ *
|
|
+ * The atomic_reset hook is optional. When not implemented the core
|
|
+ * allocates a new state and calls &__drm_atomic_helper_bridge_reset().
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * A valid drm_bridge_state object in case of success, an ERR_PTR()
|
|
+ * giving the reason of the failure otherwise.
|
|
+ */
|
|
+ struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
|
|
};
|
|
|
|
/**
|
|
@@ -380,6 +442,8 @@ struct drm_bridge_timings {
|
|
* struct drm_bridge - central DRM bridge control structure
|
|
*/
|
|
struct drm_bridge {
|
|
+ /** @base: inherit from &drm_private_object */
|
|
+ struct drm_private_obj base;
|
|
/** @dev: DRM device this bridge belongs to */
|
|
struct drm_device *dev;
|
|
/** @encoder: encoder to which this bridge is connected */
|
|
@@ -404,6 +468,12 @@ struct drm_bridge {
|
|
void *driver_private;
|
|
};
|
|
|
|
+static inline struct drm_bridge *
|
|
+drm_priv_to_bridge(struct drm_private_obj *priv)
|
|
+{
|
|
+ return container_of(priv, struct drm_bridge, base);
|
|
+}
|
|
+
|
|
void drm_bridge_add(struct drm_bridge *bridge);
|
|
void drm_bridge_remove(struct drm_bridge *bridge);
|
|
struct drm_bridge *of_drm_find_bridge(struct device_node *np);
|
|
@@ -491,6 +561,56 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
|
|
void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
|
|
struct drm_atomic_state *state);
|
|
|
|
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state);
|
|
+void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *state);
|
|
+struct drm_bridge_state *
|
|
+drm_atomic_helper_bridge_reset(struct drm_bridge *bridge);
|
|
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *new);
|
|
+struct drm_bridge_state *
|
|
+drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
|
|
+
|
|
+static inline struct drm_bridge_state *
|
|
+drm_atomic_get_bridge_state(struct drm_atomic_state *state,
|
|
+ struct drm_bridge *bridge)
|
|
+{
|
|
+ struct drm_private_state *obj_state;
|
|
+
|
|
+ obj_state = drm_atomic_get_private_obj_state(state, &bridge->base);
|
|
+ if (IS_ERR(obj_state))
|
|
+ return ERR_CAST(obj_state);
|
|
+
|
|
+ return drm_priv_to_bridge_state(obj_state);
|
|
+}
|
|
+
|
|
+static inline struct drm_bridge_state *
|
|
+drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
|
|
+ struct drm_bridge *bridge)
|
|
+{
|
|
+ struct drm_private_state *obj_state;
|
|
+
|
|
+ obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base);
|
|
+ if (!obj_state)
|
|
+ return NULL;
|
|
+
|
|
+ return drm_priv_to_bridge_state(obj_state);
|
|
+}
|
|
+
|
|
+static inline struct drm_bridge_state *
|
|
+drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
|
|
+ struct drm_bridge *bridge)
|
|
+{
|
|
+ struct drm_private_state *obj_state;
|
|
+
|
|
+ obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base);
|
|
+ if (!obj_state)
|
|
+ return NULL;
|
|
+
|
|
+ return drm_priv_to_bridge_state(obj_state);
|
|
+}
|
|
+
|
|
#ifdef CONFIG_DRM_PANEL_BRIDGE
|
|
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
|
|
struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 7b00bdecec40941c08461791cdc3ebfe766ffb15 Mon Sep 17 00:00:00 2001
|
|
From: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Date: Thu, 19 Dec 2019 11:11:49 +0100
|
|
Subject: [PATCH] drm/bridge: Patch atomic hooks to take a drm_bridge_state
|
|
|
|
This way the drm_bridge_funcs interface is consistent with the rest of
|
|
the subsystem.
|
|
|
|
The only driver implementing those hooks (analogix DP) is patched too.
|
|
|
|
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
.../drm/bridge/analogix/analogix_dp_core.c | 41 +++++++------
|
|
drivers/gpu/drm/drm_bridge.c | 61 +++++++++++++++----
|
|
include/drm/drm_bridge.h | 8 +--
|
|
3 files changed, 77 insertions(+), 33 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
|
index 6effe532f820..6fab71985cd4 100644
|
|
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
|
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
|
@@ -1289,19 +1289,21 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
|
|
return conn_state->crtc;
|
|
}
|
|
|
|
-static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *state)
|
|
+static void
|
|
+analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_bridge_state)
|
|
{
|
|
+ struct drm_atomic_state *old_state = old_bridge_state->base.state;
|
|
struct analogix_dp_device *dp = bridge->driver_private;
|
|
struct drm_crtc *crtc;
|
|
struct drm_crtc_state *old_crtc_state;
|
|
int ret;
|
|
|
|
- crtc = analogix_dp_get_new_crtc(dp, state);
|
|
+ crtc = analogix_dp_get_new_crtc(dp, old_state);
|
|
if (!crtc)
|
|
return;
|
|
|
|
- old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
|
|
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
|
|
/* Don't touch the panel if we're coming back from PSR */
|
|
if (old_crtc_state && old_crtc_state->self_refresh_active)
|
|
return;
|
|
@@ -1366,20 +1368,22 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
|
|
return ret;
|
|
}
|
|
|
|
-static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *state)
|
|
+static void
|
|
+analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_bridge_state)
|
|
{
|
|
+ struct drm_atomic_state *old_state = old_bridge_state->base.state;
|
|
struct analogix_dp_device *dp = bridge->driver_private;
|
|
struct drm_crtc *crtc;
|
|
struct drm_crtc_state *old_crtc_state;
|
|
int timeout_loop = 0;
|
|
int ret;
|
|
|
|
- crtc = analogix_dp_get_new_crtc(dp, state);
|
|
+ crtc = analogix_dp_get_new_crtc(dp, old_state);
|
|
if (!crtc)
|
|
return;
|
|
|
|
- old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
|
|
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
|
|
/* Not a full enable, just disable PSR and continue */
|
|
if (old_crtc_state && old_crtc_state->self_refresh_active) {
|
|
ret = analogix_dp_disable_psr(dp);
|
|
@@ -1440,18 +1444,20 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
|
|
dp->dpms_mode = DRM_MODE_DPMS_OFF;
|
|
}
|
|
|
|
-static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *state)
|
|
+static void
|
|
+analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_bridge_state)
|
|
{
|
|
+ struct drm_atomic_state *old_state = old_bridge_state->base.state;
|
|
struct analogix_dp_device *dp = bridge->driver_private;
|
|
struct drm_crtc *crtc;
|
|
struct drm_crtc_state *new_crtc_state = NULL;
|
|
|
|
- crtc = analogix_dp_get_new_crtc(dp, state);
|
|
+ crtc = analogix_dp_get_new_crtc(dp, old_state);
|
|
if (!crtc)
|
|
goto out;
|
|
|
|
- new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
+ new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
|
|
if (!new_crtc_state)
|
|
goto out;
|
|
|
|
@@ -1463,20 +1469,21 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
|
|
analogix_dp_bridge_disable(bridge);
|
|
}
|
|
|
|
-static
|
|
-void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *state)
|
|
+static void
|
|
+analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_bridge_state)
|
|
{
|
|
+ struct drm_atomic_state *old_state = old_bridge_state->base.state;
|
|
struct analogix_dp_device *dp = bridge->driver_private;
|
|
struct drm_crtc *crtc;
|
|
struct drm_crtc_state *new_crtc_state;
|
|
int ret;
|
|
|
|
- crtc = analogix_dp_get_new_crtc(dp, state);
|
|
+ crtc = analogix_dp_get_new_crtc(dp, old_state);
|
|
if (!crtc)
|
|
return;
|
|
|
|
- new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
+ new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
|
|
if (!new_crtc_state || !new_crtc_state->self_refresh_active)
|
|
return;
|
|
|
|
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
|
|
index a3921b45f044..6bdc4ab789c9 100644
|
|
--- a/drivers/gpu/drm/drm_bridge.c
|
|
+++ b/drivers/gpu/drm/drm_bridge.c
|
|
@@ -465,10 +465,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
|
|
|
|
encoder = bridge->encoder;
|
|
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
- if (iter->funcs->atomic_disable)
|
|
- iter->funcs->atomic_disable(iter, old_state);
|
|
- else if (iter->funcs->disable)
|
|
+ if (iter->funcs->atomic_disable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ iter);
|
|
+ if (WARN_ON(!old_bridge_state))
|
|
+ return;
|
|
+
|
|
+ iter->funcs->atomic_disable(iter, old_bridge_state);
|
|
+ } else if (iter->funcs->disable) {
|
|
iter->funcs->disable(iter);
|
|
+ }
|
|
|
|
if (iter == bridge)
|
|
break;
|
|
@@ -499,10 +508,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
|
|
|
|
encoder = bridge->encoder;
|
|
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
|
|
- if (bridge->funcs->atomic_post_disable)
|
|
- bridge->funcs->atomic_post_disable(bridge, old_state);
|
|
- else if (bridge->funcs->post_disable)
|
|
+ if (bridge->funcs->atomic_post_disable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ bridge);
|
|
+ if (WARN_ON(!old_bridge_state))
|
|
+ return;
|
|
+
|
|
+ bridge->funcs->atomic_post_disable(bridge,
|
|
+ old_bridge_state);
|
|
+ } else if (bridge->funcs->post_disable) {
|
|
bridge->funcs->post_disable(bridge);
|
|
+ }
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
|
|
@@ -531,10 +550,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
|
|
|
|
encoder = bridge->encoder;
|
|
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
- if (iter->funcs->atomic_pre_enable)
|
|
- iter->funcs->atomic_pre_enable(iter, old_state);
|
|
- else if (iter->funcs->pre_enable)
|
|
+ if (iter->funcs->atomic_pre_enable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ iter);
|
|
+ if (WARN_ON(!old_bridge_state))
|
|
+ return;
|
|
+
|
|
+ iter->funcs->atomic_pre_enable(iter, old_bridge_state);
|
|
+ } else if (iter->funcs->pre_enable) {
|
|
iter->funcs->pre_enable(iter);
|
|
+ }
|
|
|
|
if (iter == bridge)
|
|
break;
|
|
@@ -564,10 +592,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
|
|
|
|
encoder = bridge->encoder;
|
|
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
|
|
- if (bridge->funcs->atomic_enable)
|
|
- bridge->funcs->atomic_enable(bridge, old_state);
|
|
- else if (bridge->funcs->enable)
|
|
+ if (bridge->funcs->atomic_enable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ bridge);
|
|
+ if (WARN_ON(!old_bridge_state))
|
|
+ return;
|
|
+
|
|
+ bridge->funcs->atomic_enable(bridge, old_bridge_state);
|
|
+ } else if (bridge->funcs->enable) {
|
|
bridge->funcs->enable(bridge);
|
|
+ }
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
|
|
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
|
|
index 8a926c1a08db..0331e330635b 100644
|
|
--- a/include/drm/drm_bridge.h
|
|
+++ b/include/drm/drm_bridge.h
|
|
@@ -282,7 +282,7 @@ struct drm_bridge_funcs {
|
|
* The @atomic_pre_enable callback is optional.
|
|
*/
|
|
void (*atomic_pre_enable)(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *old_state);
|
|
+ struct drm_bridge_state *old_bridge_state);
|
|
|
|
/**
|
|
* @atomic_enable:
|
|
@@ -307,7 +307,7 @@ struct drm_bridge_funcs {
|
|
* The @atomic_enable callback is optional.
|
|
*/
|
|
void (*atomic_enable)(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *old_state);
|
|
+ struct drm_bridge_state *old_bridge_state);
|
|
/**
|
|
* @atomic_disable:
|
|
*
|
|
@@ -330,7 +330,7 @@ struct drm_bridge_funcs {
|
|
* The @atomic_disable callback is optional.
|
|
*/
|
|
void (*atomic_disable)(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *old_state);
|
|
+ struct drm_bridge_state *old_bridge_state);
|
|
|
|
/**
|
|
* @atomic_post_disable:
|
|
@@ -356,7 +356,7 @@ struct drm_bridge_funcs {
|
|
* The @atomic_post_disable callback is optional.
|
|
*/
|
|
void (*atomic_post_disable)(struct drm_bridge *bridge,
|
|
- struct drm_atomic_state *old_state);
|
|
+ struct drm_bridge_state *old_bridge_state);
|
|
|
|
/**
|
|
* @atomic_duplicate_state:
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 7891bb247f22d65b57b84efb5cadaae13114236b Mon Sep 17 00:00:00 2001
|
|
From: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Date: Thu, 19 Dec 2019 11:11:50 +0100
|
|
Subject: [PATCH] drm/bridge: Add an ->atomic_check() hook
|
|
|
|
So that bridge drivers have a way to check/reject an atomic operation.
|
|
The drm_atomic_bridge_chain_check() (which is just a wrapper around
|
|
the ->atomic_check() hook) is called in place of
|
|
drm_bridge_chain_mode_fixup() (when ->atomic_check() is not implemented,
|
|
the core falls back on ->mode_fixup(), so the behavior should stay
|
|
the same for existing bridge drivers).
|
|
|
|
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/drm_atomic_helper.c | 12 +++---
|
|
drivers/gpu/drm/drm_bridge.c | 62 +++++++++++++++++++++++++++++
|
|
include/drm/drm_bridge.h | 29 +++++++++++++-
|
|
3 files changed, 96 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
|
|
index ad8eae98d9e8..afe14f72a824 100644
|
|
--- a/drivers/gpu/drm/drm_atomic_helper.c
|
|
+++ b/drivers/gpu/drm/drm_atomic_helper.c
|
|
@@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state)
|
|
funcs = encoder->helper_private;
|
|
|
|
bridge = drm_bridge_chain_get_first_bridge(encoder);
|
|
- ret = drm_bridge_chain_mode_fixup(bridge,
|
|
- &new_crtc_state->mode,
|
|
- &new_crtc_state->adjusted_mode);
|
|
- if (!ret) {
|
|
- DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
|
|
- return -EINVAL;
|
|
+ ret = drm_atomic_bridge_chain_check(bridge,
|
|
+ new_crtc_state,
|
|
+ new_conn_state);
|
|
+ if (ret) {
|
|
+ DRM_DEBUG_ATOMIC("Bridge atomic check failed\n");
|
|
+ return ret;
|
|
}
|
|
|
|
if (funcs && funcs->atomic_check) {
|
|
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
|
|
index 6bdc4ab789c9..442804598f60 100644
|
|
--- a/drivers/gpu/drm/drm_bridge.c
|
|
+++ b/drivers/gpu/drm/drm_bridge.c
|
|
@@ -609,6 +609,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
|
|
|
|
+static int drm_atomic_bridge_check(struct drm_bridge *bridge,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state)
|
|
+{
|
|
+ if (bridge->funcs->atomic_check) {
|
|
+ struct drm_bridge_state *bridge_state;
|
|
+ int ret;
|
|
+
|
|
+ bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
|
|
+ bridge);
|
|
+ if (WARN_ON(!bridge_state))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = bridge->funcs->atomic_check(bridge, bridge_state,
|
|
+ crtc_state, conn_state);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else if (bridge->funcs->mode_fixup) {
|
|
+ if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode,
|
|
+ &crtc_state->adjusted_mode))
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
|
|
+ * @bridge: bridge control structure
|
|
+ * @crtc_state: new CRTC state
|
|
+ * @conn_state: new connector state
|
|
+ *
|
|
+ * Calls &drm_bridge_funcs.atomic_check() (falls back on
|
|
+ * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
|
|
+ * starting from the last bridge to the first. These are called before calling
|
|
+ * &drm_encoder_helper_funcs.atomic_check()
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * 0 on success, a negative error code on failure
|
|
+ */
|
|
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state)
|
|
+{
|
|
+ struct drm_encoder *encoder = bridge->encoder;
|
|
+ struct drm_bridge *iter;
|
|
+
|
|
+ list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
+ int ret;
|
|
+
|
|
+ ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (iter == bridge)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
|
|
+
|
|
/**
|
|
* drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
|
|
* @bridge: the bridge this state refers to
|
|
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
|
|
index 0331e330635b..269f0d1da339 100644
|
|
--- a/include/drm/drm_bridge.h
|
|
+++ b/include/drm/drm_bridge.h
|
|
@@ -128,7 +128,9 @@ struct drm_bridge_funcs {
|
|
* this function passes all other callbacks must succeed for this
|
|
* configuration.
|
|
*
|
|
- * The @mode_fixup callback is optional.
|
|
+ * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
|
|
+ * is not called when &drm_bridge_funcs.atomic_check() is implemented,
|
|
+ * so only one of them should be provided.
|
|
*
|
|
* NOTE:
|
|
*
|
|
@@ -385,6 +387,28 @@ struct drm_bridge_funcs {
|
|
void (*atomic_destroy_state)(struct drm_bridge *bridge,
|
|
struct drm_bridge_state *state);
|
|
|
|
+ /**
|
|
+ * @atomic_check:
|
|
+ *
|
|
+ * This method is responsible for checking bridge state correctness.
|
|
+ * It can also check the state of the surrounding components in chain
|
|
+ * to make sure the whole pipeline can work properly.
|
|
+ *
|
|
+ * &drm_bridge_funcs.atomic_check() hooks are called in reverse
|
|
+ * order (from the last to the first bridge).
|
|
+ *
|
|
+ * This method is optional. &drm_bridge_funcs.mode_fixup() is not
|
|
+ * called when &drm_bridge_funcs.atomic_check() is implemented, so only
|
|
+ * one of them should be provided.
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * zero if the check passed, a negative error code otherwise.
|
|
+ */
|
|
+ int (*atomic_check)(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *bridge_state,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state);
|
|
+
|
|
/**
|
|
* @atomic_reset:
|
|
*
|
|
@@ -552,6 +576,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
|
|
void drm_bridge_chain_pre_enable(struct drm_bridge *bridge);
|
|
void drm_bridge_chain_enable(struct drm_bridge *bridge);
|
|
|
|
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state);
|
|
void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
|
|
struct drm_atomic_state *state);
|
|
void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 08c9023ffcdae0022fe84dac4e12efc6f989c6c8 Mon Sep 17 00:00:00 2001
|
|
From: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Date: Thu, 19 Dec 2019 11:11:51 +0100
|
|
Subject: [PATCH] drm/bridge: Add the necessary bits to support bus format
|
|
negotiation
|
|
|
|
drm_bridge_state is extended to describe the input and output bus
|
|
configurations. These bus configurations are exposed through the
|
|
drm_bus_cfg struct which encodes the configuration of a physical
|
|
bus between two components in an output pipeline, usually between
|
|
two bridges, an encoder and a bridge, or a bridge and a connector.
|
|
|
|
The bus configuration is stored in drm_bridge_state separately for
|
|
the input and output buses, as seen from the point of view of each
|
|
bridge. The bus configuration of a bridge output is usually identical
|
|
to the configuration of the next bridge's input, but may differ if
|
|
the signals are modified between the two bridges, for instance by an
|
|
inverter on the board. The input and output configurations of a
|
|
bridge may differ if the bridge modifies the signals internally,
|
|
for instance by performing format conversion, or*modifying signals
|
|
polarities.
|
|
|
|
Bus format negotiation is automated by the core, drivers just have
|
|
to implement the ->atomic_get_{output,input}_bus_fmts() hooks if they
|
|
want to take part to this negotiation. Negotiation happens in reverse
|
|
order, starting from the last element of the chain (the one directly
|
|
connected to the display) up to the first element of the chain (the one
|
|
connected to the encoder).
|
|
During this negotiation all supported formats are tested until we find
|
|
one that works, meaning that the formats array should be in decreasing
|
|
preference order (assuming the driver has a preference order).
|
|
|
|
Note that the bus format negotiation works even if some elements in the
|
|
chain don't implement the ->atomic_get_{output,input}_bus_fmts() hooks.
|
|
In that case, the core advertises only MEDIA_BUS_FMT_FIXED and lets
|
|
the previous bridge element decide what to do (most of the time, bridge
|
|
drivers will pick a default bus format or extract this piece of
|
|
information from somewhere else, like a FW property).
|
|
|
|
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/drm_bridge.c | 267 ++++++++++++++++++++++++++++++++++-
|
|
include/drm/drm_bridge.h | 124 ++++++++++++++++
|
|
2 files changed, 390 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
|
|
index 442804598f60..9cc4b0181f85 100644
|
|
--- a/drivers/gpu/drm/drm_bridge.c
|
|
+++ b/drivers/gpu/drm/drm_bridge.c
|
|
@@ -635,13 +635,261 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge,
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to
|
|
+ * the input end of a bridge
|
|
+ * @bridge: bridge control structure
|
|
+ * @bridge_state: new bridge state
|
|
+ * @crtc_state: new CRTC state
|
|
+ * @conn_state: new connector state
|
|
+ * @output_fmt: tested output bus format
|
|
+ * @num_input_fmts: will contain the size of the returned array
|
|
+ *
|
|
+ * This helper is a pluggable implementation of the
|
|
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't
|
|
+ * modify the bus configuration between their input and their output. It
|
|
+ * returns an array of input formats with a single element set to @output_fmt.
|
|
+ *
|
|
+ * RETURNS:
|
|
+ * a valid format array of size @num_input_fmts, or NULL if the allocation
|
|
+ * failed
|
|
+ */
|
|
+u32 *
|
|
+drm_atomic_helper_bridge_propagate_bus_fmt(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;
|
|
+
|
|
+ input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
|
|
+ if (!input_fmts) {
|
|
+ *num_input_fmts = 0;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ *num_input_fmts = 1;
|
|
+ input_fmts[0] = output_fmt;
|
|
+ return input_fmts;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
|
|
+
|
|
+static int select_bus_fmt_recursive(struct drm_bridge *first_bridge,
|
|
+ struct drm_bridge *cur_bridge,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state,
|
|
+ u32 out_bus_fmt)
|
|
+{
|
|
+ struct drm_bridge_state *cur_state;
|
|
+ unsigned int num_in_bus_fmts, i;
|
|
+ struct drm_bridge *prev_bridge;
|
|
+ u32 *in_bus_fmts;
|
|
+ int ret;
|
|
+
|
|
+ prev_bridge = drm_bridge_get_prev_bridge(cur_bridge);
|
|
+ cur_state = drm_atomic_get_new_bridge_state(crtc_state->state,
|
|
+ cur_bridge);
|
|
+ if (WARN_ON(!cur_state))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /*
|
|
+ * If bus format negotiation is not supported by this bridge, let's
|
|
+ * pass MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and
|
|
+ * hope that it can handle this situation gracefully (by providing
|
|
+ * appropriate default values).
|
|
+ */
|
|
+ if (!cur_bridge->funcs->atomic_get_input_bus_fmts) {
|
|
+ if (cur_bridge != first_bridge) {
|
|
+ ret = select_bus_fmt_recursive(first_bridge,
|
|
+ prev_bridge, crtc_state,
|
|
+ conn_state,
|
|
+ MEDIA_BUS_FMT_FIXED);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ cur_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED;
|
|
+ cur_state->output_bus_cfg.format = out_bus_fmt;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ in_bus_fmts = cur_bridge->funcs->atomic_get_input_bus_fmts(cur_bridge,
|
|
+ cur_state,
|
|
+ crtc_state,
|
|
+ conn_state,
|
|
+ out_bus_fmt,
|
|
+ &num_in_bus_fmts);
|
|
+ if (!num_in_bus_fmts)
|
|
+ return -ENOTSUPP;
|
|
+ else if (!in_bus_fmts)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (first_bridge == cur_bridge) {
|
|
+ cur_state->input_bus_cfg.format = in_bus_fmts[0];
|
|
+ cur_state->output_bus_cfg.format = out_bus_fmt;
|
|
+ kfree(in_bus_fmts);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_in_bus_fmts; i++) {
|
|
+ ret = select_bus_fmt_recursive(first_bridge, prev_bridge,
|
|
+ crtc_state, conn_state,
|
|
+ in_bus_fmts[i]);
|
|
+ if (ret != -ENOTSUPP)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!ret) {
|
|
+ cur_state->input_bus_cfg.format = in_bus_fmts[i];
|
|
+ cur_state->output_bus_cfg.format = out_bus_fmt;
|
|
+ }
|
|
+
|
|
+ kfree(in_bus_fmts);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function is called by &drm_atomic_bridge_chain_check() just before
|
|
+ * calling &drm_bridge_funcs.atomic_check() on all elements of the chain.
|
|
+ * It performs bus format negotiation between bridge elements. The negotiation
|
|
+ * happens in reverse order, starting from the last element in the chain up to
|
|
+ * @bridge.
|
|
+ *
|
|
+ * Negotiation starts by retrieving supported output bus formats on the last
|
|
+ * bridge element and testing them one by one. The test is recursive, meaning
|
|
+ * that for each tested output format, the whole chain will be walked backward,
|
|
+ * and each element will have to choose an input bus format that can be
|
|
+ * transcoded to the requested output format. When a bridge element does not
|
|
+ * support transcoding into a specific output format -ENOTSUPP is returned and
|
|
+ * the next bridge element will have to try a different format. If none of the
|
|
+ * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail.
|
|
+ *
|
|
+ * This implementation is relying on
|
|
+ * &drm_bridge_funcs.atomic_get_output_bus_fmts() and
|
|
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported
|
|
+ * input/output formats.
|
|
+ *
|
|
+ * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by
|
|
+ * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts()
|
|
+ * tries a single format: &drm_connector.display_info.bus_formats[0] if
|
|
+ * available, MEDIA_BUS_FMT_FIXED otherwise.
|
|
+ *
|
|
+ * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented,
|
|
+ * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the
|
|
+ * bridge element that lacks this hook and asks the previous element in the
|
|
+ * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what
|
|
+ * to do in that case (fail if they want to enforce bus format negotiation, or
|
|
+ * provide a reasonable default if they need to support pipelines where not
|
|
+ * all elements support bus format negotiation).
|
|
+ */
|
|
+static int
|
|
+drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state)
|
|
+{
|
|
+ struct drm_connector *conn = conn_state->connector;
|
|
+ struct drm_encoder *encoder = bridge->encoder;
|
|
+ struct drm_bridge_state *last_bridge_state;
|
|
+ unsigned int i, num_out_bus_fmts;
|
|
+ struct drm_bridge *last_bridge;
|
|
+ u32 *out_bus_fmts;
|
|
+ int ret = 0;
|
|
+
|
|
+ last_bridge = list_last_entry(&encoder->bridge_chain,
|
|
+ struct drm_bridge, chain_node);
|
|
+ last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
|
|
+ last_bridge);
|
|
+ if (WARN_ON(!last_bridge_state))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (last_bridge->funcs->atomic_get_output_bus_fmts) {
|
|
+ const struct drm_bridge_funcs *funcs = last_bridge->funcs;
|
|
+
|
|
+ out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge,
|
|
+ last_bridge_state,
|
|
+ crtc_state,
|
|
+ conn_state,
|
|
+ &num_out_bus_fmts);
|
|
+ if (!num_out_bus_fmts)
|
|
+ return -ENOTSUPP;
|
|
+ else if (!out_bus_fmts)
|
|
+ return -ENOMEM;
|
|
+ } else {
|
|
+ num_out_bus_fmts = 1;
|
|
+ out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL);
|
|
+ if (!out_bus_fmts)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (conn->display_info.num_bus_formats &&
|
|
+ conn->display_info.bus_formats)
|
|
+ out_bus_fmts[0] = conn->display_info.bus_formats[0];
|
|
+ else
|
|
+ out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_out_bus_fmts; i++) {
|
|
+ ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state,
|
|
+ conn_state, out_bus_fmts[i]);
|
|
+ if (ret != -ENOTSUPP)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ kfree(out_bus_fmts);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void
|
|
+drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
|
|
+ struct drm_connector *conn,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+ struct drm_bridge_state *bridge_state, *next_bridge_state;
|
|
+ struct drm_bridge *next_bridge;
|
|
+ u32 output_flags;
|
|
+
|
|
+ bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
|
|
+ next_bridge = drm_bridge_get_next_bridge(bridge);
|
|
+
|
|
+ /*
|
|
+ * Let's try to apply the most common case here, that is, propagate
|
|
+ * display_info flags for the last bridge, and propagate the input
|
|
+ * flags of the next bridge element to the output end of the current
|
|
+ * bridge when the bridge is not the last one.
|
|
+ * There are exceptions to this rule, like when signal inversion is
|
|
+ * happening at the board level, but that's something drivers can deal
|
|
+ * with from their &drm_bridge_funcs.atomic_check() implementation by
|
|
+ * simply overriding the flags value we've set here.
|
|
+ */
|
|
+ if (!next_bridge) {
|
|
+ output_flags = conn->display_info.bus_flags;
|
|
+ } else {
|
|
+ next_bridge_state = drm_atomic_get_new_bridge_state(state,
|
|
+ next_bridge);
|
|
+ output_flags = next_bridge_state->input_bus_cfg.flags;
|
|
+ }
|
|
+
|
|
+ bridge_state->output_bus_cfg.flags = output_flags;
|
|
+
|
|
+ /*
|
|
+ * Propage the output flags to the input end of the bridge. Again, it's
|
|
+ * not necessarily what all bridges want, but that's what most of them
|
|
+ * do, and by doing that by default we avoid forcing drivers to
|
|
+ * duplicate the "dummy propagation" logic.
|
|
+ */
|
|
+ bridge_state->input_bus_cfg.flags = output_flags;
|
|
+}
|
|
+
|
|
/**
|
|
* drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
|
|
* @bridge: bridge control structure
|
|
* @crtc_state: new CRTC state
|
|
* @conn_state: new connector state
|
|
*
|
|
- * Calls &drm_bridge_funcs.atomic_check() (falls back on
|
|
+ * First trigger a bus format negotiation before calling
|
|
+ * &drm_bridge_funcs.atomic_check() (falls back on
|
|
* &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
|
|
* starting from the last bridge to the first. These are called before calling
|
|
* &drm_encoder_helper_funcs.atomic_check()
|
|
@@ -653,12 +901,29 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
|
|
struct drm_crtc_state *crtc_state,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
+ struct drm_connector *conn = conn_state->connector;
|
|
struct drm_encoder *encoder = bridge->encoder;
|
|
struct drm_bridge *iter;
|
|
+ int ret;
|
|
+
|
|
+ ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state,
|
|
+ conn_state);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
int ret;
|
|
|
|
+ /*
|
|
+ * Bus flags are propagated by default. If a bridge needs to
|
|
+ * tweak the input bus flags for any reason, it should happen
|
|
+ * in its &drm_bridge_funcs.atomic_check() implementation such
|
|
+ * that preceding bridges in the chain can propagate the new
|
|
+ * bus flags.
|
|
+ */
|
|
+ drm_atomic_bridge_propagate_bus_flags(iter, conn,
|
|
+ crtc_state->state);
|
|
+
|
|
ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
|
|
index 269f0d1da339..192227f03d4b 100644
|
|
--- a/include/drm/drm_bridge.h
|
|
+++ b/include/drm/drm_bridge.h
|
|
@@ -35,6 +35,38 @@ struct drm_bridge;
|
|
struct drm_bridge_timings;
|
|
struct drm_panel;
|
|
|
|
+/**
|
|
+ * struct drm_bus_cfg - bus configuration
|
|
+ *
|
|
+ * This structure stores the configuration of a physical bus between two
|
|
+ * components in an output pipeline, usually between two bridges, an encoder
|
|
+ * and a bridge, or a bridge and a connector.
|
|
+ *
|
|
+ * The bus configuration is stored in &drm_bridge_state separately for the
|
|
+ * input and output buses, as seen from the point of view of each bridge. The
|
|
+ * bus configuration of a bridge output is usually identical to the
|
|
+ * configuration of the next bridge's input, but may differ if the signals are
|
|
+ * modified between the two bridges, for instance by an inverter on the board.
|
|
+ * The input and output configurations of a bridge may differ if the bridge
|
|
+ * modifies the signals internally, for instance by performing format
|
|
+ * conversion, or modifying signals polarities.
|
|
+ */
|
|
+struct drm_bus_cfg {
|
|
+ /**
|
|
+ * @fmt: format used on this bus (one of the MEDIA_BUS_FMT_* format)
|
|
+ *
|
|
+ * This field should not be directly modified by drivers
|
|
+ * (&drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus
|
|
+ * format negotiation).
|
|
+ */
|
|
+ u32 format;
|
|
+
|
|
+ /**
|
|
+ * @flags: DRM_BUS_* flags used on this bus
|
|
+ */
|
|
+ u32 flags;
|
|
+};
|
|
+
|
|
/**
|
|
* struct drm_bridge_state - Atomic bridge state object
|
|
* @base: inherit from &drm_private_state
|
|
@@ -44,6 +76,16 @@ struct drm_bridge_state {
|
|
struct drm_private_state base;
|
|
|
|
struct drm_bridge *bridge;
|
|
+
|
|
+ /**
|
|
+ * @input_bus_cfg: input bus configuration
|
|
+ */
|
|
+ struct drm_bus_cfg input_bus_cfg;
|
|
+
|
|
+ /**
|
|
+ * @output_bus_cfg: input bus configuration
|
|
+ */
|
|
+ struct drm_bus_cfg output_bus_cfg;
|
|
};
|
|
|
|
static inline struct drm_bridge_state *
|
|
@@ -387,6 +429,72 @@ struct drm_bridge_funcs {
|
|
void (*atomic_destroy_state)(struct drm_bridge *bridge,
|
|
struct drm_bridge_state *state);
|
|
|
|
+ /**
|
|
+ * @atomic_get_output_bus_fmts:
|
|
+ *
|
|
+ * Return the supported bus formats on the output end of a bridge.
|
|
+ * The returned array must be allocated with kmalloc() and will be
|
|
+ * freed by the caller. If the allocation fails, NULL should be
|
|
+ * returned. num_output_fmts must be set to the returned array size.
|
|
+ * Formats listed in the returned array should be listed in decreasing
|
|
+ * preference order (the core will try all formats until it finds one
|
|
+ * that works).
|
|
+ *
|
|
+ * This method is only called on the last element of the bridge chain
|
|
+ * as part of the bus format negotiation process that happens in
|
|
+ * &drm_atomic_bridge_chain_select_bus_fmts().
|
|
+ * This method is optional. When not implemented, the core will
|
|
+ * fall back to &drm_connector.display_info.bus_formats[0] if
|
|
+ * &drm_connector.display_info.num_bus_formats > 0,
|
|
+ * or to MEDIA_BUS_FMT_FIXED otherwise.
|
|
+ */
|
|
+ u32 *(*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);
|
|
+
|
|
+ /**
|
|
+ * @atomic_get_input_bus_fmts:
|
|
+ *
|
|
+ * Return the supported bus formats on the input end of a bridge for
|
|
+ * a specific output bus format.
|
|
+ *
|
|
+ * The returned array must be allocated with kmalloc() and will be
|
|
+ * freed by the caller. If the allocation fails, NULL should be
|
|
+ * returned. num_output_fmts must be set to the returned array size.
|
|
+ * Formats listed in the returned array should be listed in decreasing
|
|
+ * preference order (the core will try all formats until it finds one
|
|
+ * that works). When the format is not supported NULL should be
|
|
+ * returned and *num_output_fmts should be set to 0.
|
|
+ *
|
|
+ * This method is called on all elements of the bridge chain as part of
|
|
+ * the bus format negotiation process that happens in
|
|
+ * &drm_atomic_bridge_chain_select_bus_fmts().
|
|
+ * This method is optional. When not implemented, the core will bypass
|
|
+ * bus format negotiation on this element of the bridge without
|
|
+ * failing, and the previous element in the chain will be passed
|
|
+ * MEDIA_BUS_FMT_FIXED as its output bus format.
|
|
+ *
|
|
+ * Bridge drivers that need to support being linked to bridges that are
|
|
+ * not supporting bus format negotiation should handle the
|
|
+ * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a
|
|
+ * sensible default value or extracting this information from somewhere
|
|
+ * else (FW property, &drm_display_mode, &drm_display_info, ...)
|
|
+ *
|
|
+ * Note: Even if input format selection on the first bridge has no
|
|
+ * impact on the negotiation process (bus format negotiation stops once
|
|
+ * we reach the first element of the chain), drivers are expected to
|
|
+ * return accurate input formats as the input format may be used to
|
|
+ * configure the CRTC output appropriately.
|
|
+ */
|
|
+ u32 *(*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);
|
|
+
|
|
/**
|
|
* @atomic_check:
|
|
*
|
|
@@ -401,6 +509,14 @@ struct drm_bridge_funcs {
|
|
* called when &drm_bridge_funcs.atomic_check() is implemented, so only
|
|
* one of them should be provided.
|
|
*
|
|
+ * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or
|
|
+ * &drm_bridge_state.output_bus_cfg.flags it should should happen in
|
|
+ * this function. By default the &drm_bridge_state.output_bus_cfg.flags
|
|
+ * field is set to the next bridge
|
|
+ * &drm_bridge_state.input_bus_cfg.flags value or
|
|
+ * &drm_connector.display_info.bus_flags if the bridge is the last
|
|
+ * element in the chain.
|
|
+ *
|
|
* RETURNS:
|
|
* zero if the check passed, a negative error code otherwise.
|
|
*/
|
|
@@ -588,6 +704,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
|
|
void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
|
|
struct drm_atomic_state *state);
|
|
|
|
+u32 *
|
|
+drm_atomic_helper_bridge_propagate_bus_fmt(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);
|
|
+
|
|
void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
|
|
struct drm_bridge_state *state);
|
|
void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 152ee505b889cb0f5bc3ad4af82b43a8f4f6562b Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Wed, 18 Dec 2019 16:46:28 +0100
|
|
Subject: [PATCH] drm/bridge: dw-hdmi: set mtmdsclock for deep color
|
|
|
|
Configure the correct mtmdsclock for deep colors to prepare support
|
|
for 10, 12 & 16bit output.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 17 +++++++++++++++++
|
|
1 file changed, 17 insertions(+)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index 24965e53d351..b8b91d28f398 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -1824,9 +1824,26 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
|
|
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
|
|
|
|
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
|
|
+ switch (hdmi_bus_fmt_color_depth(
|
|
+ hdmi->hdmi_data.enc_out_bus_format)) {
|
|
+ case 16:
|
|
+ vmode->mtmdsclock = (u64)vmode->mpixelclock * 2;
|
|
+ break;
|
|
+ case 12:
|
|
+ vmode->mtmdsclock = (u64)vmode->mpixelclock * 3 / 2;
|
|
+ break;
|
|
+ case 10:
|
|
+ vmode->mtmdsclock = (u64)vmode->mpixelclock * 5 / 4;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
vmode->mtmdsclock /= 2;
|
|
|
|
+ dev_dbg(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock);
|
|
+
|
|
/* Set up HDMI_FC_INVIDCONF */
|
|
inv_val = (hdmi->hdmi_data.hdcp_enable ||
|
|
(dw_hdmi_support_scdc(hdmi) &&
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 7cc0932dda90b853ff45cbe43688d5888a81d0b7 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Wed, 18 Dec 2019 16:46:29 +0100
|
|
Subject: [PATCH] drm/bridge: dw-hdmi: add max bpc connector property
|
|
|
|
Add the max_bpc property to the dw-hdmi connector to prepare support
|
|
for 10, 12 & 16bit output support.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index b8b91d28f398..8da173798ed4 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -2412,6 +2412,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
|
|
DRM_MODE_CONNECTOR_HDMIA,
|
|
hdmi->ddc);
|
|
|
|
+ drm_atomic_helper_connector_reset(connector);
|
|
+
|
|
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
|
|
+
|
|
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);
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From d38c69cb5bb12563a7a533ae5eb75453e736f85b Mon Sep 17 00:00:00 2001
|
|
From: Neil Armstrong <narmstrong@baylibre.com>
|
|
Date: Wed, 18 Dec 2019 16:46:30 +0100
|
|
Subject: [PATCH] drm/bridge: synopsys: dw-hdmi: add bus format negociation
|
|
|
|
Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
|
|
the possible output and input formats for the current mode and monitor,
|
|
and use the negotiated formats in a basic atomic_check callback.
|
|
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
|
|
1 file changed, 268 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index 8da173798ed4..88da301dc086 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -2101,11 +2101,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|
hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
|
|
hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
|
|
|
|
- /* TOFIX: Get input format from plat data or fallback to RGB888 */
|
|
if (hdmi->plat_data->input_bus_format)
|
|
hdmi->hdmi_data.enc_in_bus_format =
|
|
hdmi->plat_data->input_bus_format;
|
|
- else
|
|
+ else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
|
|
hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
|
|
/* TOFIX: Get input encoding from plat data or fallback to none */
|
|
@@ -2115,8 +2114,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|
else
|
|
hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
|
|
|
|
- /* TOFIX: Default to RGB888 output format */
|
|
- hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
+ if (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
|
|
+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
|
|
hdmi->hdmi_data.pix_repet_factor = 0;
|
|
hdmi->hdmi_data.hdcp_enable = 0;
|
|
@@ -2394,6 +2393,267 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
|
|
.atomic_check = dw_hdmi_connector_atomic_check,
|
|
};
|
|
|
|
+/*
|
|
+ * 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 12 possible output formats for a mode/connector */
|
|
+#define MAX_OUTPUT_SEL_FORMATS 12
|
|
+
|
|
+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;
|
|
+ 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 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
|
|
+ */
|
|
+
|
|
+ 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;
|
|
+
|
|
+ /* Default 8bit RGB fallback */
|
|
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_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 4 possible input formats for an output format */
|
|
+#define MAX_INPUT_SEL_FORMATS 4
|
|
+
|
|
+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;
|
|
+ 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) {
|
|
+ /* 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;
|
|
+
|
|
+ /* 420 */
|
|
+ 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;
|
|
+
|
|
+ dev_dbg(hdmi->dev, "selected output format %x\n",
|
|
+ bridge_state->output_bus_cfg.format);
|
|
+
|
|
+ hdmi->hdmi_data.enc_out_bus_format =
|
|
+ bridge_state->output_bus_cfg.format;
|
|
+
|
|
+ dev_dbg(hdmi->dev, "selected input format %x\n",
|
|
+ bridge_state->input_bus_cfg.format);
|
|
+
|
|
+ hdmi->hdmi_data.enc_in_bus_format =
|
|
+ bridge_state->input_bus_cfg.format;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
|
|
{
|
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
|
@@ -2502,6 +2762,9 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
|
|
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
|
|
.attach = dw_hdmi_bridge_attach,
|
|
.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,
|
|
.enable = dw_hdmi_bridge_enable,
|
|
.disable = dw_hdmi_bridge_disable,
|
|
.mode_set = dw_hdmi_bridge_mode_set,
|
|
@@ -2966,6 +3229,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
|
|
|
hdmi->bridge.driver_private = hdmi;
|
|
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
|
|
+
|
|
#ifdef CONFIG_OF
|
|
hdmi->bridge.of_node = pdev->dev.of_node;
|
|
#endif
|
|
--
|
|
2.17.1
|
|
|
|
|
|
From 9c8d9cb1a3fa8b7cfe1052c1e6e549c294f65f69 Mon Sep 17 00:00:00 2001
|
|
From: Neil Armstrong <narmstrong@baylibre.com>
|
|
Date: Wed, 18 Dec 2019 16:46:31 +0100
|
|
Subject: [PATCH] drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >=
|
|
0x200a
|
|
|
|
Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support
|
|
for these modes in the connector if the platform supports them.
|
|
We limit these modes to DW-HDMI IP version >= 0x200a which
|
|
are designed to support HDMI2.0 display modes.
|
|
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
---
|
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++
|
|
include/drm/bridge/dw_hdmi.h | 1 +
|
|
2 files changed, 7 insertions(+)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index 88da301dc086..5749bdaba95b 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -3234,6 +3234,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
|
hdmi->bridge.of_node = pdev->dev.of_node;
|
|
#endif
|
|
|
|
+ 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;
|
|
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
|
|
index 9d4d5cc47969..0b34a12c4a1c 100644
|
|
--- a/include/drm/bridge/dw_hdmi.h
|
|
+++ b/include/drm/bridge/dw_hdmi.h
|
|
@@ -129,6 +129,7 @@ struct dw_hdmi_plat_data {
|
|
unsigned long input_bus_format;
|
|
unsigned long input_bus_encoding;
|
|
bool use_drm_infoframe;
|
|
+ bool ycbcr_420_allowed;
|
|
|
|
/* Vendor PHY support */
|
|
const struct dw_hdmi_phy_ops *phy_ops;
|
|
--
|
|
2.17.1
|
|
|