mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
3219 lines
112 KiB
Diff
3219 lines
112 KiB
Diff
From dc4eaa80dc3753167b487d80e60b38b936eea07e Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Mon, 18 Feb 2019 20:17:40 +0000
|
|
Subject: [PATCH] [media] rc/keymaps: add keytable for Pine64 IR Remote
|
|
Controller
|
|
|
|
This RC map is based on remote key schema at [1], the mouse button key
|
|
did not have an obvious target and was mapped to KEY_CONTEXT_MENU.
|
|
|
|
[1] http://files.pine64.org/doc/Pine%20A64%20Schematic/remote-wit-logo.jpg
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/media/rc/keymaps/Makefile | 1 +
|
|
drivers/media/rc/keymaps/rc-pine64.c | 59 ++++++++++++++++++++++++++++++++++++
|
|
include/media/rc-map.h | 1 +
|
|
3 files changed, 61 insertions(+)
|
|
create mode 100644 drivers/media/rc/keymaps/rc-pine64.c
|
|
|
|
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
|
|
index a56fc634d2d6..7b24df2ecd93 100644
|
|
--- a/drivers/media/rc/keymaps/Makefile
|
|
+++ b/drivers/media/rc/keymaps/Makefile
|
|
@@ -78,6 +78,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 afd2ab31bdf2..d420172fb911 100644
|
|
--- a/include/media/rc-map.h
|
|
+++ b/include/media/rc-map.h
|
|
@@ -231,6 +231,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"
|
|
|
|
From 45fb9a7ad0baf6bb5537d40d02379690f6011acf 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 543363102d03..42bf1334f464 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 {
|
|
|
|
From f895f1f4de0b7252f29e7e5779727b303df9e58d Mon Sep 17 00:00:00 2001
|
|
From: schaecsn <schaecsn@gmx.net>
|
|
Date: Sun, 17 Nov 2019 21:24:13 -0800
|
|
Subject: [PATCH] thermal: rockchip: enable hwmon
|
|
|
|
Enable hwmon for the soc and gpu temperature sensors.
|
|
|
|
Signed-off-by: Stefan Schaeckeler <schaecsn@gmx.net>
|
|
---
|
|
drivers/thermal/rockchip_thermal.c | 12 +++++++++++-
|
|
1 file changed, 11 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
|
|
index 343c2f5c5a25..e47c60010259 100644
|
|
--- a/drivers/thermal/rockchip_thermal.c
|
|
+++ b/drivers/thermal/rockchip_thermal.c
|
|
@@ -19,6 +19,8 @@
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
+#include "thermal_hwmon.h"
|
|
+
|
|
/**
|
|
* If the temperature over a period of time High,
|
|
* the resulting TSHUT gave CRU module,let it reset the entire chip,
|
|
@@ -1321,8 +1323,15 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
|
|
|
thermal->chip->control(thermal->regs, true);
|
|
|
|
- for (i = 0; i < thermal->chip->chn_num; i++)
|
|
+ for (i = 0; i < thermal->chip->chn_num; i++) {
|
|
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
|
|
+ thermal->sensors[i].tzd->tzp->no_hwmon = false;
|
|
+ error = thermal_add_hwmon_sysfs(thermal->sensors[i].tzd);
|
|
+ if (error)
|
|
+ dev_warn(&pdev->dev,
|
|
+ "failed to register sensor %d with hwmon: %d\n",
|
|
+ i, error);
|
|
+ }
|
|
|
|
platform_set_drvdata(pdev, thermal);
|
|
|
|
@@ -1344,6 +1353,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
|
|
for (i = 0; i < thermal->chip->chn_num; i++) {
|
|
struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
|
|
|
|
+ thermal_remove_hwmon_sysfs(sensor->tzd);
|
|
rockchip_thermal_toggle_sensor(sensor, false);
|
|
}
|
|
|
|
|
|
From b5bcb54fe763aa9f40bae7af7e0629a3a6b93a52 Mon Sep 17 00:00:00 2001
|
|
From: Robin Murphy <robin.murphy@arm.com>
|
|
Date: Thu, 28 Nov 2019 20:59:27 +0000
|
|
Subject: [PATCH] arm64: dts: rockchip: Add GPU cooling device for RK3399
|
|
|
|
As for RK3288, now that we have a binding for the GPU we can
|
|
hook up the missing cooling device for the thermal zone.
|
|
|
|
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
|
|
---
|
|
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 9 +++++++++
|
|
1 file changed, 9 insertions(+)
|
|
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
index e62ea0e2b657..aa0838adb224 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
@@ -828,6 +828,14 @@
|
|
type = "critical";
|
|
};
|
|
};
|
|
+
|
|
+ cooling-maps {
|
|
+ map0 {
|
|
+ trip = <&gpu_alert0>;
|
|
+ cooling-device =
|
|
+ <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
|
+ };
|
|
+ };
|
|
};
|
|
};
|
|
|
|
@@ -1887,6 +1895,7 @@
|
|
<GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
interrupt-names = "gpu", "job", "mmu";
|
|
clocks = <&cru ACLK_GPU>;
|
|
+ #cooling-cells = <2>;
|
|
power-domains = <&power RK3399_PD_GPU>;
|
|
status = "disabled";
|
|
};
|
|
|
|
From 3a1811868a98b2178563a986b628dbaca9bdf210 Mon Sep 17 00:00:00 2001
|
|
From: "redchenjs@live.com" <redchenjs@live.com>
|
|
Date: Sun, 1 Dec 2019 06:28:26 +0000
|
|
Subject: [PATCH] ARM: dts: rockchip: Add missing cpu operating points for
|
|
rk3288-tinker
|
|
|
|
The generic operating points specified in rk3288.dtsi are specified by
|
|
Rockchip as conservative and for all cases.
|
|
|
|
The Tinker Board / S devices use a special chip variant called rk3288-c
|
|
and use different operating points with a higher max frequency.
|
|
|
|
So add the missing operating points for Tinker Board / S devices, also
|
|
increase the regulator-max-microvolt to 1400000 so that the cpu can
|
|
operate at 1.8 GHz.
|
|
|
|
Signed-off-by: Jack Chen <redchenjs@live.com>
|
|
---
|
|
arch/arm/boot/dts/rk3288-tinker.dtsi | 13 ++++++++++++-
|
|
1 file changed, 12 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/arch/arm/boot/dts/rk3288-tinker.dtsi b/arch/arm/boot/dts/rk3288-tinker.dtsi
|
|
index 0aeef23ca3db..312582c1bd37 100644
|
|
--- a/arch/arm/boot/dts/rk3288-tinker.dtsi
|
|
+++ b/arch/arm/boot/dts/rk3288-tinker.dtsi
|
|
@@ -113,6 +113,17 @@
|
|
cpu0-supply = <&vdd_cpu>;
|
|
};
|
|
|
|
+&cpu_opp_table {
|
|
+ opp-1704000000 {
|
|
+ opp-hz = /bits/ 64 <1704000000>;
|
|
+ opp-microvolt = <1350000>;
|
|
+ };
|
|
+ opp-1800000000 {
|
|
+ opp-hz = /bits/ 64 <1800000000>;
|
|
+ opp-microvolt = <1400000>;
|
|
+ };
|
|
+};
|
|
+
|
|
&gmac {
|
|
assigned-clocks = <&cru SCLK_MAC>;
|
|
assigned-clock-parents = <&ext_gmac>;
|
|
@@ -175,7 +186,7 @@
|
|
regulator-always-on;
|
|
regulator-boot-on;
|
|
regulator-min-microvolt = <750000>;
|
|
- regulator-max-microvolt = <1350000>;
|
|
+ regulator-max-microvolt = <1400000>;
|
|
regulator-name = "vdd_arm";
|
|
regulator-ramp-delay = <6000>;
|
|
regulator-state-mem {
|
|
|
|
From 933ddafeb4621f0e2cdf18022223115569e4659e 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 9340435a94a0..266bf704b0fd 100644
|
|
--- a/drivers/media/cec/cec-adap.c
|
|
+++ b/drivers/media/cec/cec-adap.c
|
|
@@ -1595,8 +1595,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 db7adffcdc76..24405e96e785 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);
|
|
@@ -250,6 +256,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)
|
|
@@ -288,6 +305,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 7bdf855aaecd..e8b1fc0a22c4 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 0a4f69cc9dd4..3048b3f2219c 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;
|
|
|
|
From 943aab648dbd4061511b0f4de544af28710127dc Mon Sep 17 00:00:00 2001
|
|
From: Wright Feng <wright.feng@cypress.com>
|
|
Date: Thu, 12 Dec 2019 00:52:45 +0100
|
|
Subject: [PATCH] brcmfmac: reset two D11 cores if chip has two D11 cores
|
|
|
|
There are two D11 cores in RSDB chips like 4359. We have to reset two
|
|
D11 cores simutaneously before firmware download, or the firmware may
|
|
not be initialized correctly and cause "fw initialized failed" error.
|
|
|
|
Signed-off-by: Wright Feng <wright.feng@cypress.com>
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Reviewed-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
.../wireless/broadcom/brcm80211/brcmfmac/chip.c | 50 ++++++++++++++++++++++
|
|
.../wireless/broadcom/brcm80211/brcmfmac/chip.h | 1 +
|
|
.../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2 +-
|
|
3 files changed, 52 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
index dd586a96b57a..5e11b30ae337 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
@@ -433,11 +433,25 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
|
|
{
|
|
struct brcmf_chip_priv *ci;
|
|
int count;
|
|
+ struct brcmf_core *d11core2 = NULL;
|
|
+ struct brcmf_core_priv *d11priv2 = NULL;
|
|
|
|
ci = core->chip;
|
|
|
|
+ /* special handle two D11 cores reset */
|
|
+ if (core->pub.id == BCMA_CORE_80211) {
|
|
+ d11core2 = brcmf_chip_get_d11core(&ci->pub, 1);
|
|
+ if (d11core2) {
|
|
+ brcmf_dbg(INFO, "found two d11 cores, reset both\n");
|
|
+ d11priv2 = container_of(d11core2,
|
|
+ struct brcmf_core_priv, pub);
|
|
+ }
|
|
+ }
|
|
+
|
|
/* must disable first to work for arbitrary current core state */
|
|
brcmf_chip_ai_coredisable(core, prereset, reset);
|
|
+ if (d11priv2)
|
|
+ brcmf_chip_ai_coredisable(d11priv2, prereset, reset);
|
|
|
|
count = 0;
|
|
while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
|
|
@@ -449,9 +463,30 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
|
|
usleep_range(40, 60);
|
|
}
|
|
|
|
+ if (d11priv2) {
|
|
+ count = 0;
|
|
+ while (ci->ops->read32(ci->ctx,
|
|
+ d11priv2->wrapbase + BCMA_RESET_CTL) &
|
|
+ BCMA_RESET_CTL_RESET) {
|
|
+ ci->ops->write32(ci->ctx,
|
|
+ d11priv2->wrapbase + BCMA_RESET_CTL,
|
|
+ 0);
|
|
+ count++;
|
|
+ if (count > 50)
|
|
+ break;
|
|
+ usleep_range(40, 60);
|
|
+ }
|
|
+ }
|
|
+
|
|
ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
|
|
postreset | BCMA_IOCTL_CLK);
|
|
ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
|
|
+
|
|
+ if (d11priv2) {
|
|
+ ci->ops->write32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL,
|
|
+ postreset | BCMA_IOCTL_CLK);
|
|
+ ci->ops->read32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL);
|
|
+ }
|
|
}
|
|
|
|
char *brcmf_chip_name(u32 id, u32 rev, char *buf, uint len)
|
|
@@ -1113,6 +1148,21 @@ void brcmf_chip_detach(struct brcmf_chip *pub)
|
|
kfree(chip);
|
|
}
|
|
|
|
+struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit)
|
|
+{
|
|
+ struct brcmf_chip_priv *chip;
|
|
+ struct brcmf_core_priv *core;
|
|
+
|
|
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
|
|
+ list_for_each_entry(core, &chip->cores, list) {
|
|
+ if (core->pub.id == BCMA_CORE_80211) {
|
|
+ if (unit-- == 0)
|
|
+ return &core->pub;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
|
|
{
|
|
struct brcmf_chip_priv *chip;
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
|
|
index 7b00f6a59e89..8fa38658e727 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
|
|
@@ -74,6 +74,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx,
|
|
const struct brcmf_buscore_ops *ops);
|
|
void brcmf_chip_detach(struct brcmf_chip *chip);
|
|
struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
|
|
+struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit);
|
|
struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
|
|
struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
|
|
bool brcmf_chip_iscoreup(struct brcmf_core *core);
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
|
|
index 6c463475e90b..259b9e8f4300 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
|
|
@@ -78,7 +78,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
|
|
BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
|
|
};
|
|
|
|
-#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
|
|
+#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */
|
|
|
|
#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
|
|
|
|
|
|
From 17d07d4ceeb0ecfb0f7df2a6936fe3a8c48f4a60 Mon Sep 17 00:00:00 2001
|
|
From: Chung-Hsien Hsu <stanley.hsu@cypress.com>
|
|
Date: Thu, 12 Dec 2019 00:52:46 +0100
|
|
Subject: [PATCH] brcmfmac: set F2 blocksize and watermark for 4359
|
|
|
|
Set F2 blocksize to 256 bytes and watermark to 0x40 for 4359. Also
|
|
enable and configure F1 MesBusyCtrl. It fixes DMA error while having
|
|
UDP bi-directional traffic.
|
|
|
|
Signed-off-by: Chung-Hsien Hsu <stanley.hsu@cypress.com>
|
|
[slightly adapted for rebase on mainline linux]
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Reviewed-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 6 +++++-
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 15 +++++++++++++++
|
|
2 files changed, 20 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
index fc12598b2dd3..cf171a2aea35 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
@@ -43,6 +43,7 @@
|
|
|
|
#define SDIO_FUNC1_BLOCKSIZE 64
|
|
#define SDIO_FUNC2_BLOCKSIZE 512
|
|
+#define SDIO_4359_FUNC2_BLOCKSIZE 256
|
|
/* Maximum milliseconds to wait for F2 to come up */
|
|
#define SDIO_WAIT_F2RDY 3000
|
|
|
|
@@ -903,6 +904,7 @@ static void brcmf_sdiod_host_fixup(struct mmc_host *host)
|
|
static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
|
|
{
|
|
int ret = 0;
|
|
+ unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE;
|
|
|
|
sdio_claim_host(sdiodev->func1);
|
|
|
|
@@ -912,7 +914,9 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
|
|
sdio_release_host(sdiodev->func1);
|
|
goto out;
|
|
}
|
|
- ret = sdio_set_block_size(sdiodev->func2, SDIO_FUNC2_BLOCKSIZE);
|
|
+ if (sdiodev->func2->device == SDIO_DEVICE_ID_BROADCOM_4359)
|
|
+ f2_blksz = SDIO_4359_FUNC2_BLOCKSIZE;
|
|
+ ret = sdio_set_block_size(sdiodev->func2, f2_blksz);
|
|
if (ret) {
|
|
brcmf_err("Failed to set F2 blocksize\n");
|
|
sdio_release_host(sdiodev->func1);
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
index 264ad63232f8..21e535072f3f 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
@@ -42,6 +42,8 @@
|
|
#define DEFAULT_F2_WATERMARK 0x8
|
|
#define CY_4373_F2_WATERMARK 0x40
|
|
#define CY_43012_F2_WATERMARK 0x60
|
|
+#define CY_4359_F2_WATERMARK 0x40
|
|
+#define CY_4359_F1_MESBUSYCTRL (CY_4359_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
|
|
|
|
#ifdef DEBUG
|
|
|
|
@@ -4205,6 +4207,19 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
|
|
brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl,
|
|
&err);
|
|
break;
|
|
+ case SDIO_DEVICE_ID_BROADCOM_4359:
|
|
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
|
+ CY_4359_F2_WATERMARK);
|
|
+ brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
|
|
+ CY_4359_F2_WATERMARK, &err);
|
|
+ devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL,
|
|
+ &err);
|
|
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
|
|
+ brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl,
|
|
+ &err);
|
|
+ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL,
|
|
+ CY_4359_F1_MESBUSYCTRL, &err);
|
|
+ break;
|
|
default:
|
|
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
|
|
DEFAULT_F2_WATERMARK, &err);
|
|
|
|
From 674ac87ce4558b0425bfd05a8816c2163ff0df26 Mon Sep 17 00:00:00 2001
|
|
From: Soeren Moch <smoch@web.de>
|
|
Date: Thu, 12 Dec 2019 00:52:47 +0100
|
|
Subject: [PATCH] brcmfmac: fix rambase for 4359/9
|
|
|
|
Newer 4359 chip revisions need a different rambase address.
|
|
This fixes firmware download on such devices which fails otherwise.
|
|
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Acked-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 3 ++-
|
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
index 5e11b30ae337..36c913893b6d 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
@@ -712,7 +712,6 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
|
|
case BRCM_CC_43569_CHIP_ID:
|
|
case BRCM_CC_43570_CHIP_ID:
|
|
case BRCM_CC_4358_CHIP_ID:
|
|
- case BRCM_CC_4359_CHIP_ID:
|
|
case BRCM_CC_43602_CHIP_ID:
|
|
case BRCM_CC_4371_CHIP_ID:
|
|
return 0x180000;
|
|
@@ -722,6 +721,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
|
|
case BRCM_CC_4366_CHIP_ID:
|
|
case BRCM_CC_43664_CHIP_ID:
|
|
return 0x200000;
|
|
+ case BRCM_CC_4359_CHIP_ID:
|
|
+ return (ci->pub.chiprev < 9) ? 0x180000 : 0x160000;
|
|
case CY_CC_4373_CHIP_ID:
|
|
return 0x160000;
|
|
default:
|
|
|
|
From eb76633d7081bab5437d2d481b7e47eeb29f0e57 Mon Sep 17 00:00:00 2001
|
|
From: Soeren Moch <smoch@web.de>
|
|
Date: Thu, 12 Dec 2019 00:52:48 +0100
|
|
Subject: [PATCH] brcmfmac: make errors when setting roaming parameters
|
|
non-fatal
|
|
|
|
4359 dongles do not support setting roaming parameters (error -52).
|
|
Do not fail the 80211 configuration in this case.
|
|
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Acked-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 10 ++++------
|
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
index e3ebb7abbdae..3d2fb64f4deb 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
@@ -5944,19 +5944,17 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
|
|
roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
|
|
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
|
|
(void *)roamtrigger, sizeof(roamtrigger));
|
|
- if (err) {
|
|
+ if (err)
|
|
bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);
|
|
- goto roam_setup_done;
|
|
- }
|
|
|
|
roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
|
|
roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
|
|
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
|
|
(void *)roam_delta, sizeof(roam_delta));
|
|
- if (err) {
|
|
+ if (err)
|
|
bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);
|
|
- goto roam_setup_done;
|
|
- }
|
|
+
|
|
+ return 0;
|
|
|
|
roam_setup_done:
|
|
return err;
|
|
|
|
From 292a234f90d08ff79afb33171e6474d77eb2484a Mon Sep 17 00:00:00 2001
|
|
From: Soeren Moch <smoch@web.de>
|
|
Date: Thu, 12 Dec 2019 00:52:49 +0100
|
|
Subject: [PATCH] brcmfmac: add support for BCM4359 SDIO chipset
|
|
|
|
BCM4359 is a 2x2 802.11 abgn+ac Dual-Band HT80 combo chip and it
|
|
supports Real Simultaneous Dual Band feature.
|
|
|
|
Based on a similar patch by: Wright Feng <wright.feng@cypress.com>
|
|
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Acked-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
---
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 ++
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 1 +
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 ++
|
|
include/linux/mmc/sdio_ids.h | 2 ++
|
|
4 files changed, 7 insertions(+)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
index cf171a2aea35..2bd71aae4899 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
@@ -973,8 +973,10 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
|
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455),
|
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
|
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
|
|
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
|
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
|
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
|
|
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359),
|
|
{ /* end: all zeroes */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
index 36c913893b6d..1a8394c3c901 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
|
|
@@ -1412,6 +1412,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
|
|
addr = CORE_CC_REG(base, sr_control0);
|
|
reg = chip->ops->read32(chip->ctx, addr);
|
|
return (reg & CC_SR_CTL0_ENABLE_MASK) != 0;
|
|
+ case BRCM_CC_4359_CHIP_ID:
|
|
case CY_CC_43012_CHIP_ID:
|
|
addr = CORE_CC_REG(pmu->base, retention_ctl);
|
|
reg = chip->ops->read32(chip->ctx, addr);
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
index 21e535072f3f..c4012ed58b9c 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
|
|
@@ -616,6 +616,7 @@ BRCMF_FW_DEF(43455, "brcmfmac43455-sdio");
|
|
BRCMF_FW_DEF(43456, "brcmfmac43456-sdio");
|
|
BRCMF_FW_DEF(4354, "brcmfmac4354-sdio");
|
|
BRCMF_FW_DEF(4356, "brcmfmac4356-sdio");
|
|
+BRCMF_FW_DEF(4359, "brcmfmac4359-sdio");
|
|
BRCMF_FW_DEF(4373, "brcmfmac4373-sdio");
|
|
BRCMF_FW_DEF(43012, "brcmfmac43012-sdio");
|
|
|
|
@@ -638,6 +639,7 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
|
|
BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455),
|
|
BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
|
|
BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
|
|
+ BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
|
|
BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
|
|
BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012)
|
|
};
|
|
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
|
|
index d1a5d5df02f5..d51a31a59f16 100644
|
|
--- a/include/linux/mmc/sdio_ids.h
|
|
+++ b/include/linux/mmc/sdio_ids.h
|
|
@@ -41,8 +41,10 @@
|
|
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
|
|
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
|
|
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
|
|
+#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359
|
|
#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
|
|
#define SDIO_DEVICE_ID_CYPRESS_43012 43012
|
|
+#define SDIO_DEVICE_ID_CYPRESS_89359 0x4355
|
|
|
|
#define SDIO_VENDOR_ID_INTEL 0x0089
|
|
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
|
|
|
|
From 6c9b1c4f22fa084c22e63775b48734a232e3cd39 Mon Sep 17 00:00:00 2001
|
|
From: Wright Feng <wright.feng@cypress.com>
|
|
Date: Thu, 12 Dec 2019 00:52:50 +0100
|
|
Subject: [PATCH] brcmfmac: add RSDB condition when setting interface
|
|
combinations
|
|
|
|
With firmware RSDB feature
|
|
1. The maximum support interface is four.
|
|
2. The maximum difference channel is two.
|
|
3. The maximum interfaces of {station/p2p client/AP} are two.
|
|
4. The maximum interface of p2p device is one.
|
|
|
|
Signed-off-by: Wright Feng <wright.feng@cypress.com>
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Reviewed-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 54 ++++++++++++++++++----
|
|
1 file changed, 46 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
index 3d2fb64f4deb..885201328699 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
@@ -6452,6 +6452,9 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
|
|
* #STA <= 1, #AP <= 1, channels = 1, 2 total
|
|
* #AP <= 4, matching BI, channels = 1, 4 total
|
|
*
|
|
+ * no p2p and rsdb:
|
|
+ * #STA <= 2, #AP <= 2, channels = 2, 4 total
|
|
+ *
|
|
* p2p, no mchan, and mbss:
|
|
*
|
|
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
|
|
@@ -6463,6 +6466,10 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
|
|
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
|
|
* #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
|
|
* #AP <= 4, matching BI, channels = 1, 4 total
|
|
+ *
|
|
+ * p2p, rsdb, and no mbss:
|
|
+ * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
|
|
+ * channels = 2, 4 total
|
|
*/
|
|
static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
|
|
{
|
|
@@ -6470,13 +6477,14 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
|
|
struct ieee80211_iface_limit *c0_limits = NULL;
|
|
struct ieee80211_iface_limit *p2p_limits = NULL;
|
|
struct ieee80211_iface_limit *mbss_limits = NULL;
|
|
- bool mbss, p2p;
|
|
+ bool mbss, p2p, rsdb;
|
|
int i, c, n_combos;
|
|
|
|
mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
|
|
p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
|
|
+ rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
|
|
|
|
- n_combos = 1 + !!p2p + !!mbss;
|
|
+ n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
|
|
combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
|
|
if (!combo)
|
|
goto err;
|
|
@@ -6487,16 +6495,36 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
|
|
|
|
c = 0;
|
|
i = 0;
|
|
- c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
|
|
+ if (p2p && rsdb)
|
|
+ c0_limits = kcalloc(4, sizeof(*c0_limits), GFP_KERNEL);
|
|
+ else if (p2p)
|
|
+ c0_limits = kcalloc(3, sizeof(*c0_limits), GFP_KERNEL);
|
|
+ else
|
|
+ c0_limits = kcalloc(2, sizeof(*c0_limits), GFP_KERNEL);
|
|
if (!c0_limits)
|
|
goto err;
|
|
- c0_limits[i].max = 1;
|
|
- c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
|
|
- if (p2p) {
|
|
+ if (p2p && rsdb) {
|
|
+ combo[c].num_different_channels = 2;
|
|
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
+ BIT(NL80211_IFTYPE_P2P_GO) |
|
|
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
|
|
+ c0_limits[i].max = 2;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
|
|
+ c0_limits[i].max = 1;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
|
|
+ c0_limits[i].max = 2;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
+ BIT(NL80211_IFTYPE_P2P_GO);
|
|
+ c0_limits[i].max = 2;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
|
|
+ combo[c].max_interfaces = 5;
|
|
+ } else if (p2p) {
|
|
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
|
|
combo[c].num_different_channels = 2;
|
|
else
|
|
combo[c].num_different_channels = 1;
|
|
+ c0_limits[i].max = 1;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
|
|
wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO) |
|
|
BIT(NL80211_IFTYPE_P2P_DEVICE);
|
|
@@ -6505,16 +6533,26 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
|
|
c0_limits[i].max = 1;
|
|
c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO);
|
|
+ combo[c].max_interfaces = i;
|
|
+ } else if (rsdb) {
|
|
+ combo[c].num_different_channels = 2;
|
|
+ c0_limits[i].max = 2;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
|
|
+ c0_limits[i].max = 2;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
|
|
+ combo[c].max_interfaces = 3;
|
|
} else {
|
|
combo[c].num_different_channels = 1;
|
|
c0_limits[i].max = 1;
|
|
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
|
|
+ c0_limits[i].max = 1;
|
|
c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
|
|
+ combo[c].max_interfaces = i;
|
|
}
|
|
- combo[c].max_interfaces = i;
|
|
combo[c].n_limits = i;
|
|
combo[c].limits = c0_limits;
|
|
|
|
- if (p2p) {
|
|
+ if (p2p && !rsdb) {
|
|
c++;
|
|
i = 0;
|
|
p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
|
|
|
|
From 8c3545f6af6eddc0ef18ef985d96697dac65acc2 Mon Sep 17 00:00:00 2001
|
|
From: Wright Feng <wright.feng@cypress.com>
|
|
Date: Thu, 12 Dec 2019 00:52:51 +0100
|
|
Subject: [PATCH] brcmfmac: not set mbss in vif if firmware does not support
|
|
MBSS
|
|
|
|
With RSDB mode, FMAC and firmware are able to create 2 or more AP,
|
|
so we should not set mbss in vif structure if firmware does not
|
|
support MBSS feature.
|
|
|
|
Signed-off-by: Wright Feng <wright.feng@cypress.com>
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
Reviewed-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
|
|
---
|
|
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 +++-
|
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
index 885201328699..8020cf933c5d 100644
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
|
@@ -5301,6 +5301,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
|
|
struct brcmf_cfg80211_vif *vif_walk;
|
|
struct brcmf_cfg80211_vif *vif;
|
|
bool mbss;
|
|
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
|
|
|
|
brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
|
|
sizeof(*vif));
|
|
@@ -5313,7 +5314,8 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
|
|
|
|
brcmf_init_prof(&vif->profile);
|
|
|
|
- if (type == NL80211_IFTYPE_AP) {
|
|
+ if (type == NL80211_IFTYPE_AP &&
|
|
+ brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
|
|
mbss = false;
|
|
list_for_each_entry(vif_walk, &cfg->vif_list, list) {
|
|
if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
|
|
|
|
From f0ba3ec80e9aff664dfd1def2c14caab9fd72f44 Mon Sep 17 00:00:00 2001
|
|
From: Soeren Moch <smoch@web.de>
|
|
Date: Thu, 12 Dec 2019 00:52:52 +0100
|
|
Subject: [PATCH] arm64: dts: rockchip: RockPro64: enable wifi module at sdio0
|
|
|
|
RockPro64 supports an Ampak AP6359SA based wifi/bt combo module.
|
|
The BCM4359/9 wifi controller in this module is connected to sdio0,
|
|
enable this interface.
|
|
|
|
Use the in-band sdio irq instead of the out-of-band wifi_host_wake_l
|
|
signal since the latter is not working reliably on this board (probably
|
|
due to it's PCIe WAKE# connection).
|
|
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
---
|
|
arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts | 21 ++++++++++++++-------
|
|
1 file changed, 14 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
index 7f4b2eba31d4..9fa92790d6e0 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
@@ -71,13 +71,6 @@
|
|
clock-names = "ext_clock";
|
|
pinctrl-names = "default";
|
|
pinctrl-0 = <&wifi_enable_h>;
|
|
-
|
|
- /*
|
|
- * On the module itself this is one of these (depending
|
|
- * on the actual card populated):
|
|
- * - SDIO_RESET_L_WL_REG_ON
|
|
- * - PDN (power down when low)
|
|
- */
|
|
reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
|
|
};
|
|
|
|
@@ -650,6 +643,20 @@
|
|
status = "okay";
|
|
};
|
|
|
|
+&sdio0 {
|
|
+ bus-width = <4>;
|
|
+ cap-sd-highspeed;
|
|
+ cap-sdio-irq;
|
|
+ disable-wp;
|
|
+ keep-power-in-suspend;
|
|
+ mmc-pwrseq = <&sdio_pwrseq>;
|
|
+ non-removable;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
|
|
+ sd-uhs-sdr104;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&sdmmc {
|
|
bus-width = <4>;
|
|
cap-sd-highspeed;
|
|
|
|
From 355a3aad7ea5e41f6af92dc7e480339d3b3e008d Mon Sep 17 00:00:00 2001
|
|
From: Soeren Moch <smoch@web.de>
|
|
Date: Thu, 12 Dec 2019 00:52:53 +0100
|
|
Subject: [PATCH] arm64: dts: rockchip: RockPro64: hook up bluetooth at uart0
|
|
|
|
With enabled wifi support (required for firmware loading) for the
|
|
Ampak AP6359SA based wifi/bt combo module we now also can enable
|
|
the bluetooth part.
|
|
|
|
Suggested-by: Heiko Stuebner <heiko@sntech.de>
|
|
Signed-off-by: Soeren Moch <smoch@web.de>
|
|
---
|
|
arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts | 29 ++++++++++++++++++++++-
|
|
1 file changed, 28 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
index 9fa92790d6e0..94cc462e234d 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts
|
|
@@ -561,6 +561,20 @@
|
|
};
|
|
|
|
&pinctrl {
|
|
+ bt {
|
|
+ bt_enable_h: bt-enable-h {
|
|
+ rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+
|
|
+ bt_host_wake_l: bt-host-wake-l {
|
|
+ rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_down>;
|
|
+ };
|
|
+
|
|
+ bt_wake_l: bt-wake-l {
|
|
+ rockchip,pins = <2 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+ };
|
|
+
|
|
buttons {
|
|
pwrbtn: pwrbtn {
|
|
rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>;
|
|
@@ -729,8 +743,21 @@
|
|
|
|
&uart0 {
|
|
pinctrl-names = "default";
|
|
- pinctrl-0 = <&uart0_xfer &uart0_cts>;
|
|
+ pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;
|
|
status = "okay";
|
|
+
|
|
+ bluetooth {
|
|
+ compatible = "brcm,bcm43438-bt";
|
|
+ clocks = <&rk808 1>;
|
|
+ clock-names = "extclk";
|
|
+ device-wakeup-gpios = <&gpio2 RK_PD3 GPIO_ACTIVE_HIGH>;
|
|
+ host-wakeup-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;
|
|
+ shutdown-gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>;
|
|
+ vbat-supply = <&vcc3v3_sys>;
|
|
+ vddio-supply = <&vcc_1v8>;
|
|
+ };
|
|
};
|
|
|
|
&uart2 {
|
|
|
|
From 223c34f8b8e03e498d65bc0e25d1ebc80e30a2b9 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 14aeaf736321..02f8034f5240 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>
|
|
@@ -1015,6 +1016,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 62cb03d8f76e..69c474fc5dea 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 927e1205d7aa..1c0a08217712 100644
|
|
--- a/include/drm/drm_atomic.h
|
|
+++ b/include/drm/drm_atomic.h
|
|
@@ -660,6 +660,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 51834036a42d..2d2f213dc747 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,
|
|
u32 connector_type);
|
|
|
|
From 49582f19875394b4266647e54c0131f03a282a70 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>
|
|
---
|
|
drivers/gpu/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 bb411fe52ae8..c8b77b6a8fb0 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 2d2f213dc747..07d7f1a59d50 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:
|
|
|
|
From 502e34d5acec737bacdcdf1bf225a965251cd37e 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 69c474fc5dea..4b2eb18ec1a7 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 07d7f1a59d50..b63b4cf7383d 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,
|
|
|
|
From 859dc9e46f14b11382e0fbc1f015c08568517f65 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 b63b4cf7383d..c4fd15503aee 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,
|
|
|
|
From 7c862ad0a16a7a5b2b8a81737fe9bd9c06391c41 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 67fca439bbfb..9e0927d22db6 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -1818,9 +1818,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) &&
|
|
|
|
From d38a4b3a81733ba3c262dafac74887006c3bc0ff 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 9e0927d22db6..051001f77dd4 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -2406,6 +2406,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);
|
|
|
|
From fbf1c57734a89545c689100116c6bb5d296881b7 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 051001f77dd4..80066c65f91d 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -2095,11 +2095,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 */
|
|
@@ -2109,8 +2108,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;
|
|
@@ -2388,6 +2387,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;
|
|
@@ -2496,6 +2756,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,
|
|
@@ -2960,6 +3223,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
|
|
|
|
From 046a2dcb296543b7f3309e968afee8e3c56af5ce 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 80066c65f91d..0e716c40c42c 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -3228,6 +3228,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;
|
|
|
|
From d4cd48f629aa37e58e2e6e1e4323763702c641d0 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Mon, 23 Dec 2019 08:49:19 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: round clock rate down to closest
|
|
1000 Hz
|
|
|
|
Commit 287422a95fe2 ("drm/rockchip: Round up _before_ giving to the clock framework")
|
|
changed what rate clk_round_rate() is called with, an additional 999 Hz
|
|
added to the requsted mode clock. This has caused a regression on RK3328
|
|
and presumably also on RK3228 because the inno-hdmi-phy clock requires an
|
|
exact match of the requested rate in the pre pll config table.
|
|
|
|
When an exact match is not found the parent clock rate (24MHz) is returned
|
|
to the clk_round_rate() caller. This cause wrong pixel clock to be used and
|
|
result in no-signal when configuring a mode on RK3328.
|
|
|
|
Fix this by rounding the rate down to closest 1000 Hz in round_rate func,
|
|
this allows an exact match to be found in pre pll config table.
|
|
|
|
Fixes: 287422a95fe2 ("drm/rockchip: Round up _before_ giving to the clock framework")
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 2b97fb1185a0..9ca20c947283 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -603,6 +603,8 @@ static long inno_hdmi_phy_rk3228_clk_round_rate(struct clk_hw *hw,
|
|
{
|
|
const struct pre_pll_config *cfg = pre_pll_cfg_table;
|
|
|
|
+ rate = (rate / 1000) * 1000;
|
|
+
|
|
for (; cfg->pixclock != 0; cfg++)
|
|
if (cfg->pixclock == rate && !cfg->fracdiv)
|
|
break;
|
|
@@ -755,6 +757,8 @@ static long inno_hdmi_phy_rk3328_clk_round_rate(struct clk_hw *hw,
|
|
{
|
|
const struct pre_pll_config *cfg = pre_pll_cfg_table;
|
|
|
|
+ rate = (rate / 1000) * 1000;
|
|
+
|
|
for (; cfg->pixclock != 0; cfg++)
|
|
if (cfg->pixclock == rate)
|
|
break;
|