summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2022-06-15 20:12:17 +0300
committerDaniel Vetter <daniel.vetter@ffwll.ch>2022-06-15 20:12:17 +0300
commit0f95ee9a0c579ebed0309657f6918673927189f2 (patch)
treee10c39634b67eaaae5d6330f1f3538b24251439c
parentb13baccc3850ca8b8cccbf8ed9912dbaa0fdf7f3 (diff)
parentdfa687bffc8a4a21ed929c7dececf01b8f1f52ee (diff)
downloadlinux-0f95ee9a0c579ebed0309657f6918673927189f2.tar.xz
Merge tag 'drm-misc-next-2022-06-08' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.20: UAPI Changes: * connector: export bpc limits in debugfs * dma-buf: Print buffer name in debugfs Cross-subsystem Changes: * dma-buf: Improve dma-fence handling; Cleanups * fbdev: Device-unregistering fixes Core Changes: * client: Only use driver-validated modes to avoid blank screen * dp-aux: Make probing more reliable; Small fixes * edit: CEA data-block iterators; Introduce struct drm_edid; Many cleanups * gem: Don't use framebuffer format's non-exising color planes * probe-helper: Use 640x480 as DisplayPort fallback; Refactoring * scheduler: Don't kill jobs in interrupt context Driver Changes: * amdgpu: Use atomic fence helpers in DM; Fix VRAM address calculation; Export CRTC bpc settings via debugfs * bridge: Add TI-DLPC3433; anx7625: Fixes; fy07024di26a30d: Optional GPIO reset; icn6211: Cleanups; ldb: Add reg and reg-name properties to bindings, Kconfig fixes; lt9611: Fix display sensing; lt9611uxc: Fixes; nwl-dsi: Fixes; ps8640: Cleanups; st7735r: Fixes; tc358767: DSI/DPI refactoring and DSI-to-eDP support, Fixes; ti-sn65dsi83: Fixes; * gma500: Cleanup connector I2C handling * hyperv: Unify VRAM allocation of Gen1 and Gen2 * i915: export CRTC bpc settings via debugfs * meson: Support YUV422 output; Refcount fixes * mgag200: Support damage clipping; Support gamma handling; Protect concurrent HW access; Fixes to connector; Store model-specific limits in device-info structure; Cleanups * nouveau: Fixes and Cleanups * panel: Kconfig fixes * panfrost: Valhall support * r128: Fix bit-shift overflow * rockchip: Locking fixes in error path; Minor cleanups * ssd130x: Fix built-in linkage * ttm: Cleanups * udl; Always advertize VGA connector * fbdev/vesa: Support COMPILE_TEST Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/YqBtumw05JZDEZE2@linux-uq9g
-rw-r--r--Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml16
-rw-r--r--Documentation/devicetree/bindings/display/bridge/ingenic,jz4780-hdmi.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/ti,dlpc3433.yaml117
-rw-r--r--Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml1
-rw-r--r--Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml25
-rw-r--r--Documentation/gpu/todo.rst11
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/dma-buf/Makefile2
-rw-r--r--drivers/dma-buf/dma-buf.c4
-rw-r--r--drivers/dma-buf/dma-fence-chain.c4
-rw-r--r--drivers/dma-buf/dma-fence-unwrap.c162
-rw-r--r--drivers/dma-buf/st-dma-fence-unwrap.c157
-rw-r--r--drivers/dma-buf/sync_file.c119
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c51
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h27
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c27
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c38
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h2
-rw-r--r--drivers/gpu/drm/bridge/Kconfig17
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511.h12
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_cec.c12
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c1
-rw-r--r--drivers/gpu/drm/bridge/cdns-dsi.c26
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c42
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c24
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611uxc.c2
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c6
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c73
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c169
-rw-r--r--drivers/gpu/drm/bridge/ti-dlpc3433.c417
-rw-r--r--drivers/gpu/drm/display/drm_dp_aux_bus.c211
-rw-r--r--drivers/gpu/drm/drm_bridge.c23
-rw-r--r--drivers/gpu/drm/drm_client_modeset.c28
-rw-r--r--drivers/gpu/drm/drm_debugfs.c21
-rw-r--r--drivers/gpu/drm/drm_displayid.c16
-rw-r--r--drivers/gpu/drm/drm_edid.c1272
-rw-r--r--drivers/gpu/drm/drm_encoder.c2
-rw-r--r--drivers/gpu/drm/drm_gem_atomic_helper.c6
-rw-r--r--drivers/gpu/drm/drm_gem_framebuffer_helper.c104
-rw-r--r--drivers/gpu/drm/drm_gem_vram_helper.c54
-rw-r--r--drivers/gpu/drm/drm_mipi_dbi.c7
-rw-r--r--drivers/gpu/drm/drm_modes.c4
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c178
-rw-r--r--drivers/gpu/drm/drm_syncobj.c57
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c47
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c98
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c80
-rw-r--r--drivers/gpu/drm/gma500/intel_i2c.c36
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c50
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c36
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.h2
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_drv.h24
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c86
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c74
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c28
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.c1
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.c27
-rw-r--r--drivers/gpu/drm/mgag200/Makefile14
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c386
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.h148
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200.c200
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh.c50
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh3.c51
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200er.c46
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ev.c50
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ew3.c60
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200se.c130
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200wb.c50
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_i2c.c59
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mm.c116
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c453
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_pll.c12
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_reg.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c2
-rw-r--r--drivers/gpu/drm/panel/Kconfig2
-rw-r--r--drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c2
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c9
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c1
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_features.h13
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c18
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_issues.h19
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_regs.h1
-rw-r--r--drivers/gpu/drm/r128/r128_drv.h4
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig4
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c5
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c6
-rw-r--r--drivers/gpu/drm/solomon/ssd130x-spi.c2
-rw-r--r--drivers/gpu/drm/tiny/st7735r.c1
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c9
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c2
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c10
-rw-r--r--drivers/video/fbdev/Kconfig2
-rw-r--r--drivers/video/fbdev/core/fbmem.c22
-rw-r--r--include/drm/display/drm_dp_aux_bus.h34
-rw-r--r--include/drm/display/drm_dp_helper.h16
-rw-r--r--include/drm/drm_bridge.h1
-rw-r--r--include/drm/drm_displayid.h6
-rw-r--r--include/drm/drm_edid.h27
-rw-r--r--include/drm/drm_gem_framebuffer_helper.h10
-rw-r--r--include/drm/drm_probe_helper.h2
-rw-r--r--include/drm/gpu_scheduler.h4
-rw-r--r--include/linux/dma-fence-unwrap.h82
-rw-r--r--include/linux/fb.h1
110 files changed, 4031 insertions, 2285 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
index 77f174eee424..2ebaa43eb62e 100644
--- a/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/fsl,ldb.yaml
@@ -24,6 +24,15 @@ properties:
clock-names:
const: ldb
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ reg-names:
+ items:
+ - const: ldb
+ - const: lvds
+
ports:
$ref: /schemas/graph.yaml#/properties/ports
@@ -56,10 +65,15 @@ examples:
#include <dt-bindings/clock/imx8mp-clock.h>
blk-ctrl {
- bridge {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ bridge@5c {
compatible = "fsl,imx8mp-ldb";
clocks = <&clk IMX8MP_CLK_MEDIA_LDB>;
clock-names = "ldb";
+ reg = <0x5c 0x4>, <0x128 0x4>;
+ reg-names = "ldb", "lvds";
ports {
#address-cells = <1>;
diff --git a/Documentation/devicetree/bindings/display/bridge/ingenic,jz4780-hdmi.yaml b/Documentation/devicetree/bindings/display/bridge/ingenic,jz4780-hdmi.yaml
index b8219eab4475..89490fdffeb0 100644
--- a/Documentation/devicetree/bindings/display/bridge/ingenic,jz4780-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/ingenic,jz4780-hdmi.yaml
@@ -55,7 +55,6 @@ examples:
compatible = "ingenic,jz4780-dw-hdmi";
reg = <0x10180000 0x8000>;
reg-io-width = <4>;
- ddc-i2c-bus = <&i2c4>;
interrupt-parent = <&intc>;
interrupts = <3>;
clocks = <&cgu JZ4780_CLK_AHB0>, <&cgu JZ4780_CLK_HDMI>;
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,dlpc3433.yaml b/Documentation/devicetree/bindings/display/bridge/ti,dlpc3433.yaml
new file mode 100644
index 000000000000..542193d77cdf
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ti,dlpc3433.yaml
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/ti,dlpc3433.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI DLPC3433 MIPI DSI to DMD bridge
+
+maintainers:
+ - Jagan Teki <jagan@amarulasolutions.com>
+ - Christopher Vollo <chris@renewoutreach.org>
+
+description: |
+ TI DLPC3433 is a MIPI DSI based display controller bridge
+ for processing high resolution DMD based projectors.
+
+ It has a flexible configuration of MIPI DSI and DPI signal
+ input that produces a DMD output in RGB565, RGB666, RGB888
+ formats.
+
+ It supports upto 720p resolution with 60 and 120 Hz refresh
+ rates.
+
+properties:
+ compatible:
+ const: ti,dlpc3433
+
+ reg:
+ enum:
+ - 0x1b
+ - 0x1d
+
+ enable-gpios:
+ description: PROJ_ON pin, chip powers up PROJ_ON is high.
+
+ vcc_intf-supply:
+ description: A 1.8V/3.3V supply that power the Host I/O.
+
+ vcc_flsh-supply:
+ description: A 1.8V/3.3V supply that power the Flash I/O.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: Video port for MIPI DSI input.
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ description: array of physical DSI data lane indexes.
+ minItems: 1
+ items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for DMD output.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - enable-gpios
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bridge@1b {
+ compatible = "ti,dlpc3433";
+ reg = <0x1b>;
+ enable-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ bridge_in_dsi: endpoint {
+ remote-endpoint = <&dsi_out_bridge>;
+ data-lanes = <1 2 3 4>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ bridge_out_panel: endpoint {
+ remote-endpoint = <&panel_out_bridge>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml b/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
index 95acf9e96f1c..1cf84c8dd85e 100644
--- a/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
+++ b/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
@@ -35,7 +35,6 @@ required:
- reg
- avdd-supply
- dvdd-supply
- - reset-gpios
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
index 85f8d4764740..78964c140b46 100644
--- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
@@ -14,16 +14,21 @@ properties:
pattern: '^gpu@[a-f0-9]+$'
compatible:
- items:
- - enum:
- - amlogic,meson-g12a-mali
- - mediatek,mt8183-mali
- - realtek,rtd1619-mali
- - renesas,r9a07g044-mali
- - renesas,r9a07g054-mali
- - rockchip,px30-mali
- - rockchip,rk3568-mali
- - const: arm,mali-bifrost # Mali Bifrost GPU model/revision is fully discoverable
+ oneOf:
+ - items:
+ - enum:
+ - amlogic,meson-g12a-mali
+ - mediatek,mt8183-mali
+ - realtek,rtd1619-mali
+ - renesas,r9a07g044-mali
+ - renesas,r9a07g054-mali
+ - rockchip,px30-mali
+ - rockchip,rk3568-mali
+ - const: arm,mali-bifrost # Mali Bifrost GPU model/revision is fully discoverable
+ - items:
+ - enum:
+ - mediatek,mt8192-mali
+ - const: arm,mali-valhall-jm # Mali Valhall GPU model/revision is fully discoverable
reg:
maxItems: 1
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 10bfb50908d1..513b20ccef1e 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -617,6 +617,17 @@ Contact: Javier Martinez Canillas <javierm@redhat.com>
Level: Intermediate
+Convert Kernel Selftests (kselftest) to KUnit tests when appropriate
+--------------------------------------------------------------------
+
+Many of the `Kselftest <https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html>`_
+tests in DRM could be converted to Kunit tests instead, since that framework
+is more suitable for unit testing.
+
+Contact: Javier Martinez Canillas <javierm@redhat.com>
+
+Level: Starter
+
Enable trinity for DRM
----------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 1fc9ead83d2a..39fcbdd678a2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6462,6 +6462,7 @@ F: include/uapi/drm/savage_drm.h
DRM DRIVER FOR SIMPLE FRAMEBUFFERS
M: Thomas Zimmermann <tzimmermann@suse.de>
+M: Javier Martinez Canillas <javierm@redhat.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
@@ -6503,6 +6504,12 @@ DRM DRIVER FOR TDFX VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/tdfx/
+DRM DRIVER FOR TI DLPC3433 MIPI DSI TO DMD BRIDGE
+M: Jagan Teki <jagan@amarulasolutions.com>
+S: Maintained
+F: Documentation/devicetree/bindings/display/bridge/ti,dlpc3433.yaml
+F: drivers/gpu/drm/bridge/ti-dlpc3433.c
+
DRM DRIVER FOR TI SN65DSI86 BRIDGE CHIP
R: Douglas Anderson <dianders@chromium.org>
F: Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.yaml
@@ -6791,6 +6798,7 @@ F: drivers/gpu/drm/omapdrm/
DRM DRIVERS FOR V3D
M: Emma Anholt <emma@anholt.net>
+M: Melissa Wen <mwen@igalia.com>
S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 4c9eb53ba3f8..70ec901edf2c 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
- dma-resv.o
+ dma-fence-unwrap.o dma-resv.o
obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
obj-$(CONFIG_DMABUF_HEAPS) += heaps/
obj-$(CONFIG_SYNC_FILE) += sync_file.o
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 32f55640890c..727f5bb7d106 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -1359,7 +1359,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
return ret;
seq_puts(s, "\nDma-buf Objects:\n");
- seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\n",
+ seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n",
"size", "flags", "mode", "count", "ino");
list_for_each_entry(buf_obj, &db_list.head, list_node) {
@@ -1376,7 +1376,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
file_count(buf_obj->file),
buf_obj->exp_name,
file_inode(buf_obj->file)->i_ino,
- buf_obj->name ?: "");
+ buf_obj->name ?: "<none>");
spin_unlock(&buf_obj->name_lock);
dma_resv_describe(buf_obj->resv, s);
diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c
index 06f8ef97c6e8..a0d920576ba6 100644
--- a/drivers/dma-buf/dma-fence-chain.c
+++ b/drivers/dma-buf/dma-fence-chain.c
@@ -62,8 +62,8 @@ struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
replacement = NULL;
}
- tmp = cmpxchg((struct dma_fence __force **)&chain->prev,
- prev, replacement);
+ tmp = unrcu_pointer(cmpxchg(&chain->prev, RCU_INITIALIZER(prev),
+ RCU_INITIALIZER(replacement)));
if (tmp == prev)
dma_fence_put(tmp);
else
diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c
new file mode 100644
index 000000000000..502a65ea6d44
--- /dev/null
+++ b/drivers/dma-buf/dma-fence-unwrap.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dma-fence-util: misc functions for dma_fence objects
+ *
+ * Copyright (C) 2022 Advanced Micro Devices, Inc.
+ * Authors:
+ * Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/dma-fence.h>
+#include <linux/dma-fence-array.h>
+#include <linux/dma-fence-chain.h>
+#include <linux/dma-fence-unwrap.h>
+#include <linux/slab.h>
+
+/* Internal helper to start new array iteration, don't use directly */
+static struct dma_fence *
+__dma_fence_unwrap_array(struct dma_fence_unwrap *cursor)
+{
+ cursor->array = dma_fence_chain_contained(cursor->chain);
+ cursor->index = 0;
+ return dma_fence_array_first(cursor->array);
+}
+
+/**
+ * dma_fence_unwrap_first - return the first fence from fence containers
+ * @head: the entrypoint into the containers
+ * @cursor: current position inside the containers
+ *
+ * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
+ * first fence.
+ */
+struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head,
+ struct dma_fence_unwrap *cursor)
+{
+ cursor->chain = dma_fence_get(head);
+ return __dma_fence_unwrap_array(cursor);
+}
+EXPORT_SYMBOL_GPL(dma_fence_unwrap_first);
+
+/**
+ * dma_fence_unwrap_next - return the next fence from a fence containers
+ * @cursor: current position inside the containers
+ *
+ * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
+ * the next fence from them.
+ */
+struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
+{
+ struct dma_fence *tmp;
+
+ ++cursor->index;
+ tmp = dma_fence_array_next(cursor->array, cursor->index);
+ if (tmp)
+ return tmp;
+
+ cursor->chain = dma_fence_chain_walk(cursor->chain);
+ return __dma_fence_unwrap_array(cursor);
+}
+EXPORT_SYMBOL_GPL(dma_fence_unwrap_next);
+
+/* Implementation for the dma_fence_merge() marco, don't use directly */
+struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
+ struct dma_fence **fences,
+ struct dma_fence_unwrap *iter)
+{
+ struct dma_fence_array *result;
+ struct dma_fence *tmp, **array;
+ unsigned int i;
+ size_t count;
+
+ count = 0;
+ for (i = 0; i < num_fences; ++i) {
+ dma_fence_unwrap_for_each(tmp, &iter[i], fences[i])
+ ++count;
+ }
+
+ if (count == 0)
+ return dma_fence_get_stub();
+
+ array = kmalloc_array(count, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return NULL;
+
+ /*
+ * This trashes the input fence array and uses it as position for the
+ * following merge loop. This works because the dma_fence_merge()
+ * wrapper macro is creating this temporary array on the stack together
+ * with the iterators.
+ */
+ for (i = 0; i < num_fences; ++i)
+ fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]);
+
+ count = 0;
+ do {
+ unsigned int sel;
+
+restart:
+ tmp = NULL;
+ for (i = 0; i < num_fences; ++i) {
+ struct dma_fence *next;
+
+ while (fences[i] && dma_fence_is_signaled(fences[i]))
+ fences[i] = dma_fence_unwrap_next(&iter[i]);
+
+ next = fences[i];
+ if (!next)
+ continue;
+
+ /*
+ * We can't guarantee that inpute fences are ordered by
+ * context, but it is still quite likely when this
+ * function is used multiple times. So attempt to order
+ * the fences by context as we pass over them and merge
+ * fences with the same context.
+ */
+ if (!tmp || tmp->context > next->context) {
+ tmp = next;
+ sel = i;
+
+ } else if (tmp->context < next->context) {
+ continue;
+
+ } else if (dma_fence_is_later(tmp, next)) {
+ fences[i] = dma_fence_unwrap_next(&iter[i]);
+ goto restart;
+ } else {
+ fences[sel] = dma_fence_unwrap_next(&iter[sel]);
+ goto restart;
+ }
+ }
+
+ if (tmp) {
+ array[count++] = dma_fence_get(tmp);
+ fences[sel] = dma_fence_unwrap_next(&iter[sel]);
+ }
+ } while (tmp);
+
+ if (count == 0) {
+ tmp = dma_fence_get_stub();
+ goto return_tmp;
+ }
+
+ if (count == 1) {
+ tmp = array[0];
+ goto return_tmp;
+ }
+
+ result = dma_fence_array_create(count, array,
+ dma_fence_context_alloc(1),
+ 1, false);
+ if (!result) {
+ tmp = NULL;
+ goto return_tmp;
+ }
+ return &result->base;
+
+return_tmp:
+ kfree(array);
+ return tmp;
+}
+EXPORT_SYMBOL_GPL(__dma_fence_unwrap_merge);
diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
index 039f016b57be..4105d5ea8dde 100644
--- a/drivers/dma-buf/st-dma-fence-unwrap.c
+++ b/drivers/dma-buf/st-dma-fence-unwrap.c
@@ -4,27 +4,19 @@
* Copyright (C) 2022 Advanced Micro Devices, Inc.
*/
+#include <linux/dma-fence.h>
+#include <linux/dma-fence-array.h>
+#include <linux/dma-fence-chain.h>
#include <linux/dma-fence-unwrap.h>
-#if 0
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/mm.h>
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/random.h>
-#endif
#include "selftest.h"
#define CHAIN_SZ (4 << 10)
-static inline struct mock_fence {
+struct mock_fence {
struct dma_fence base;
spinlock_t lock;
-} *to_mock_fence(struct dma_fence *f) {
- return container_of(f, struct mock_fence, base);
-}
+};
static const char *mock_name(struct dma_fence *f)
{
@@ -45,7 +37,8 @@ static struct dma_fence *mock_fence(void)
return NULL;
spin_lock_init(&f->lock);
- dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
+ dma_fence_init(&f->base, &mock_ops, &f->lock,
+ dma_fence_context_alloc(1), 1);
return &f->base;
}
@@ -59,7 +52,7 @@ static struct dma_fence *mock_array(unsigned int num_fences, ...)
fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
if (!fences)
- return NULL;
+ goto error_put;
va_start(valist, num_fences);
for (i = 0; i < num_fences; ++i)
@@ -70,13 +63,17 @@ static struct dma_fence *mock_array(unsigned int num_fences, ...)
dma_fence_context_alloc(1),
1, false);
if (!array)
- goto cleanup;
+ goto error_free;
return &array->base;
-cleanup:
- for (i = 0; i < num_fences; ++i)
- dma_fence_put(fences[i]);
+error_free:
kfree(fences);
+
+error_put:
+ va_start(valist, num_fences);
+ for (i = 0; i < num_fences; ++i)
+ dma_fence_put(va_arg(valist, typeof(*fences)));
+ va_end(valist);
return NULL;
}
@@ -113,7 +110,6 @@ static int sanitycheck(void *arg)
if (!chain)
return -ENOMEM;
- dma_fence_signal(f);
dma_fence_put(chain);
return err;
}
@@ -154,10 +150,8 @@ static int unwrap_array(void *arg)
err = -EINVAL;
}
- dma_fence_signal(f1);
- dma_fence_signal(f2);
dma_fence_put(array);
- return 0;
+ return err;
}
static int unwrap_chain(void *arg)
@@ -196,10 +190,8 @@ static int unwrap_chain(void *arg)
err = -EINVAL;
}
- dma_fence_signal(f1);
- dma_fence_signal(f2);
dma_fence_put(chain);
- return 0;
+ return err;
}
static int unwrap_chain_array(void *arg)
@@ -242,10 +234,115 @@ static int unwrap_chain_array(void *arg)
err = -EINVAL;
}
- dma_fence_signal(f1);
- dma_fence_signal(f2);
dma_fence_put(chain);
- return 0;
+ return err;
+}
+
+static int unwrap_merge(void *arg)
+{
+ struct dma_fence *fence, *f1, *f2, *f3;
+ struct dma_fence_unwrap iter;
+ int err = 0;
+
+ f1 = mock_fence();
+ if (!f1)
+ return -ENOMEM;
+
+ f2 = mock_fence();
+ if (!f2) {
+ err = -ENOMEM;
+ goto error_put_f1;
+ }
+
+ f3 = dma_fence_unwrap_merge(f1, f2);
+ if (!f3) {
+ err = -ENOMEM;
+ goto error_put_f2;
+ }
+
+ dma_fence_unwrap_for_each(fence, &iter, f3) {
+ if (fence == f1) {
+ dma_fence_put(f1);
+ f1 = NULL;
+ } else if (fence == f2) {
+ dma_fence_put(f2);
+ f2 = NULL;
+ } else {
+ pr_err("Unexpected fence!\n");
+ err = -EINVAL;
+ }
+ }
+
+ if (f1 || f2) {
+ pr_err("Not all fences seen!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(f3);
+error_put_f2:
+ dma_fence_put(f2);
+error_put_f1:
+ dma_fence_put(f1);
+ return err;
+}
+
+static int unwrap_merge_complex(void *arg)
+{
+ struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5;
+ struct dma_fence_unwrap iter;
+ int err = -ENOMEM;
+
+ f1 = mock_fence();
+ if (!f1)
+ return -ENOMEM;
+
+ f2 = mock_fence();
+ if (!f2)
+ goto error_put_f1;
+
+ f3 = dma_fence_unwrap_merge(f1, f2);
+ if (!f3)
+ goto error_put_f2;
+
+ /* The resulting array has the fences in reverse */
+ f4 = dma_fence_unwrap_merge(f2, f1);
+ if (!f4)
+ goto error_put_f3;
+
+ /* Signaled fences should be filtered, the two arrays merged. */
+ f5 = dma_fence_unwrap_merge(f3, f4, dma_fence_get_stub());
+ if (!f5)
+ goto error_put_f4;
+
+ err = 0;
+ dma_fence_unwrap_for_each(fence, &iter, f5) {
+ if (fence == f1) {
+ dma_fence_put(f1);
+ f1 = NULL;
+ } else if (fence == f2) {
+ dma_fence_put(f2);
+ f2 = NULL;
+ } else {
+ pr_err("Unexpected fence!\n");
+ err = -EINVAL;
+ }
+ }
+
+ if (f1 || f2) {
+ pr_err("Not all fences seen!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(f5);
+error_put_f4:
+ dma_fence_put(f4);
+error_put_f3:
+ dma_fence_put(f3);
+error_put_f2:
+ dma_fence_put(f2);
+error_put_f1:
+ dma_fence_put(f1);
+ return err;
}
int dma_fence_unwrap(void)
@@ -255,6 +352,8 @@ int dma_fence_unwrap(void)
SUBTEST(unwrap_array),
SUBTEST(unwrap_chain),
SUBTEST(unwrap_chain_array),
+ SUBTEST(unwrap_merge),
+ SUBTEST(unwrap_merge_complex),
};
return subtests(tests, NULL);
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index 0fe564539166..3ebec19a8e02 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -146,50 +146,6 @@ char *sync_file_get_name(struct sync_file *sync_file, char *buf, int len)
return buf;
}
-static int sync_file_set_fence(struct sync_file *sync_file,
- struct dma_fence **fences, int num_fences)
-{
- struct dma_fence_array *array;
-
- /*
- * The reference for the fences in the new sync_file and held
- * in add_fence() during the merge procedure, so for num_fences == 1
- * we already own a new reference to the fence. For num_fence > 1
- * we own the reference of the dma_fence_array creation.
- */
-
- if (num_fences == 0) {
- sync_file->fence = dma_fence_get_stub();
- kfree(fences);
-
- } else if (num_fences == 1) {
- sync_file->fence = fences[0];
- kfree(fences);
-
- } else {
- array = dma_fence_array_create(num_fences, fences,
- dma_fence_context_alloc(1),
- 1, false);
- if (!array)
- return -ENOMEM;
-
- sync_file->fence = &array->base;
- }
-
- return 0;
-}
-
-static void add_fence(struct dma_fence **fences,
- int *i, struct dma_fence *fence)
-{
- fences[*i] = fence;
-
- if (!dma_fence_is_signaled(fence)) {
- dma_fence_get(fence);
- (*i)++;
- }
-}
-
/**
* sync_file_merge() - merge two sync_files
* @name: name of new fence
@@ -203,84 +159,21 @@ static void add_fence(struct dma_fence **fences,
static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
struct sync_file *b)
{
- struct dma_fence *a_fence, *b_fence, **fences;
- struct dma_fence_unwrap a_iter, b_iter;
- unsigned int index, num_fences;
struct sync_file *sync_file;
+ struct dma_fence *fence;
sync_file = sync_file_alloc();
if (!sync_file)
return NULL;
- num_fences = 0;
- dma_fence_unwrap_for_each(a_fence, &a_iter, a->fence)
- ++num_fences;
- dma_fence_unwrap_for_each(b_fence, &b_iter, b->fence)
- ++num_fences;
-
- if (num_fences > INT_MAX)
- goto err_free_sync_file;
-
- fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- goto err_free_sync_file;
-
- /*
- * We can't guarantee that fences in both a and b are ordered, but it is
- * still quite likely.
- *
- * So attempt to order the fences as we pass over them and merge fences
- * with the same context.
- */
-
- index = 0;
- for (a_fence = dma_fence_unwrap_first(a->fence, &a_iter),
- b_fence = dma_fence_unwrap_first(b->fence, &b_iter);
- a_fence || b_fence; ) {
-
- if (!b_fence) {
- add_fence(fences, &index, a_fence);
- a_fence = dma_fence_unwrap_next(&a_iter);
-
- } else if (!a_fence) {
- add_fence(fences, &index, b_fence);
- b_fence = dma_fence_unwrap_next(&b_iter);
-
- } else if (a_fence->context < b_fence->context) {
- add_fence(fences, &index, a_fence);
- a_fence = dma_fence_unwrap_next(&a_iter);
-
- } else if (b_fence->context < a_fence->context) {
- add_fence(fences, &index, b_fence);
- b_fence = dma_fence_unwrap_next(&b_iter);
-
- } else if (__dma_fence_is_later(a_fence->seqno, b_fence->seqno,
- a_fence->ops)) {
- add_fence(fences, &index, a_fence);
- a_fence = dma_fence_unwrap_next(&a_iter);
- b_fence = dma_fence_unwrap_next(&b_iter);
-
- } else {
- add_fence(fences, &index, b_fence);
- a_fence = dma_fence_unwrap_next(&a_iter);
- b_fence = dma_fence_unwrap_next(&b_iter);
- }
+ fence = dma_fence_unwrap_merge(a->fence, b->fence);
+ if (!fence) {
+ fput(sync_file->file);
+ return NULL;
}
-
- if (sync_file_set_fence(sync_file, fences, index) < 0)
- goto err_put_fences;
-
+ sync_file->fence = fence;
strlcpy(sync_file->user_name, name, sizeof(sync_file->user_name));
return sync_file;
-
-err_put_fences:
- while (index)
- dma_fence_put(fences[--index]);
- kfree(fences);
-
-err_free_sync_file:
- fput(sync_file->file);
- return NULL;
}
static int sync_file_release(struct inode *inode, struct file *file)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index 49e4092f447f..7a5e8a7b4a1b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -50,6 +50,35 @@ to_amdgpu_device(struct amdgpu_vram_mgr *mgr)
return container_of(mgr, struct amdgpu_device, mman.vram_mgr);
}
+static inline struct drm_buddy_block *
+amdgpu_vram_mgr_first_block(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct drm_buddy_block, link);
+}
+
+static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head)
+{
+ struct drm_buddy_block *block;
+ u64 start, size;
+
+ block = amdgpu_vram_mgr_first_block(head);
+ if (!block)
+ return false;
+
+ while (head != block->link.next) {
+ start = amdgpu_vram_mgr_block_start(block);
+ size = amdgpu_vram_mgr_block_size(block);
+
+ block = list_entry(block->link.next, struct drm_buddy_block, link);
+ if (start + size != amdgpu_vram_mgr_block_start(block))
+ return false;
+ }
+
+ return true;
+}
+
+
+
/**
* DOC: mem_info_vram_total
*
@@ -496,16 +525,22 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
list_splice_tail(trim_list, &vres->blocks);
}
- list_for_each_entry(block, &vres->blocks, link)
- vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
+ vres->base.start = 0;
+ list_for_each_entry(block, &vres->blocks, link) {
+ unsigned long start;
- block = amdgpu_vram_mgr_first_block(&vres->blocks);
- if (!block) {
- r = -EINVAL;
- goto error_fini;
- }
+ start = amdgpu_vram_mgr_block_start(block) +
+ amdgpu_vram_mgr_block_size(block);
+ start >>= PAGE_SHIFT;
+
+ if (start > vres->base.num_pages)
+ start -= vres->base.num_pages;
+ else
+ start = 0;
+ vres->base.start = max(vres->base.start, start);
- vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT;
+ vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
+ }
if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks))
vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
index 9a2db87186c7..4b267bf1c5db 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
@@ -53,33 +53,6 @@ static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block)
return PAGE_SIZE << drm_buddy_block_order(block);
}
-static inline struct drm_buddy_block *
-amdgpu_vram_mgr_first_block(struct list_head *list)
-{
- return list_first_entry_or_null(list, struct drm_buddy_block, link);
-}
-
-static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head)
-{
- struct drm_buddy_block *block;
- u64 start, size;
-
- block = amdgpu_vram_mgr_first_block(head);
- if (!block)
- return false;
-
- while (head != block->link.next) {
- start = amdgpu_vram_mgr_block_start(block);
- size = amdgpu_vram_mgr_block_size(block);
-
- block = list_entry(block->link.next, struct drm_buddy_block, link);
- if (start + size != amdgpu_vram_mgr_block_start(block))
- return false;
- }
-
- return true;
-}
-
static inline struct amdgpu_vram_mgr_resource *
to_amdgpu_vram_mgr_resource(struct ttm_resource *res)
{
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 70be67a56673..ad4571190a90 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -83,6 +83,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_vblank.h>
#include <drm/drm_audio_component.h>
+#include <drm/drm_gem_atomic_helper.h>
#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
@@ -6591,14 +6592,12 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
return &state->base;
}
-#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc)
{
crtc_debugfs_init(crtc);
return 0;
}
-#endif
static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
{
@@ -6692,9 +6691,7 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
.enable_vblank = dm_enable_vblank,
.disable_vblank = dm_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
.late_register = amdgpu_dm_crtc_late_register,
-#endif
};
static enum drm_connector_status
@@ -7598,6 +7595,10 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
goto error_unpin;
}
+ r = drm_gem_plane_helper_prepare_fb(plane, new_state);
+ if (unlikely(r != 0))
+ goto error_unpin;
+
amdgpu_bo_unreserve(rbo);
afb->address = amdgpu_bo_gpu_offset(rbo);
@@ -9132,7 +9133,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
struct dm_crtc_state *dm_old_crtc_state =
to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc));
int planes_count = 0, vpos, hpos;
- long r;
unsigned long flags;
struct amdgpu_bo *abo;
uint32_t target_vblank, last_flip_vblank;
@@ -9207,18 +9207,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
}
abo = gem_to_amdgpu_bo(fb->obj[0]);
-
- /*
- * Wait for all fences on this FB. Do limited wait to avoid
- * deadlock during GPU reset when this fence will not signal
- * but we hold reservation lock for the BO.
- */
- r = dma_resv_wait_timeout(abo->tbo.base.resv,
- DMA_RESV_USAGE_WRITE, false,
- msecs_to_jiffies(5000));
- if (unlikely(r <= 0))
- DRM_ERROR("Waiting for fences timed out!");
-
fill_dc_plane_info_and_addr(
dm->adev, new_plane_state,
afb->tiling_flags,
@@ -9561,9 +9549,14 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
int crtc_disable_count = 0;
bool mode_set_reset_required = false;
+ int r;
trace_amdgpu_dm_atomic_commit_tail_begin(state);
+ r = drm_atomic_helper_wait_for_fences(dev, state, false);
+ if (unlikely(r))
+ DRM_ERROR("Waiting for fences timed out!");
+
drm_atomic_helper_update_legacy_modeset_state(dev, state);
dm_state = dm_atomic_get_new_state(state);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
index c7a592d68feb..2a78ae007521 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
@@ -871,28 +871,18 @@ static int psr_capability_show(struct seq_file *m, void *data)
}
/*
- * Returns the current and maximum output bpc for the connector.
- * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc
+ * Returns the current bpc for the crtc.
+ * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_bpc
*/
-static int output_bpc_show(struct seq_file *m, void *data)
+static int amdgpu_current_bpc_show(struct seq_file *m, void *data)
{
- struct drm_connector *connector = m->private;
- struct drm_device *dev = connector->dev;
- struct drm_crtc *crtc = NULL;
+ struct drm_crtc *crtc = m->private;
+ struct drm_device *dev = crtc->dev;
struct dm_crtc_state *dm_crtc_state = NULL;
int res = -ENODEV;
unsigned int bpc;
mutex_lock(&dev->mode_config.mutex);
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-
- if (connector->state == NULL)
- goto unlock;
-
- crtc = connector->state->crtc;
- if (crtc == NULL)
- goto unlock;
-
drm_modeset_lock(&crtc->mutex, NULL);
if (crtc->state == NULL)
goto unlock;
@@ -922,18 +912,15 @@ static int output_bpc_show(struct seq_file *m, void *data)
}
seq_printf(m, "Current: %u\n", bpc);
- seq_printf(m, "Maximum: %u\n", connector->display_info.bpc);
res = 0;
unlock:
- if (crtc)
- drm_modeset_unlock(&crtc->mutex);
-
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ drm_modeset_unlock(&crtc->mutex);
mutex_unlock(&dev->mode_config.mutex);
return res;
}
+DEFINE_SHOW_ATTRIBUTE(amdgpu_current_bpc);
/*
* Example usage:
@@ -2539,7 +2526,6 @@ static int target_backlight_show(struct seq_file *m, void *unused)
DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support);
DEFINE_SHOW_ATTRIBUTE(dmub_fw_state);
DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer);
-DEFINE_SHOW_ATTRIBUTE(output_bpc);
DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status);
#ifdef CONFIG_DRM_AMD_DC_HDCP
DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability);
@@ -2786,7 +2772,6 @@ static const struct {
const struct file_operations *fops;
} connector_debugfs_entries[] = {
{"force_yuv420_output", &force_yuv420_output_fops},
- {"output_bpc", &output_bpc_fops},
{"trigger_hotplug", &trigger_hotplug_debugfs_fops},
{"internal_display", &internal_display_fops}
};
@@ -3170,9 +3155,10 @@ static int crc_win_update_get(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get,
crc_win_update_set, "%llu\n");
-
+#endif
void crtc_debugfs_init(struct drm_crtc *crtc)
{
+#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
struct dentry *dir = debugfs_lookup("crc", crtc->debugfs_entry);
if (!dir)
@@ -3188,9 +3174,11 @@ void crtc_debugfs_init(struct drm_crtc *crtc)
&crc_win_y_end_fops);
debugfs_create_file_unsafe("crc_win_update", 0644, dir, crtc,
&crc_win_update_fops);
-
-}
#endif
+ debugfs_create_file("amdgpu_current_bpc", 0644, crtc->debugfs_entry,
+ crtc, &amdgpu_current_bpc_fops);
+}
+
/*
* Writes DTN log state to the user supplied buffer.
* Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h
index 3366cb644053..071200473c27 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h
@@ -31,8 +31,6 @@
void connector_debugfs_init(struct amdgpu_dm_connector *connector);
void dtn_debugfs_init(struct amdgpu_device *adev);
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
void crtc_debugfs_init(struct drm_crtc *crtc);
-#endif
#endif
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 307b135da2f6..8ffd601e68f9 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -78,6 +78,7 @@ config DRM_DISPLAY_CONNECTOR
config DRM_FSL_LDB
tristate "Freescale i.MX8MP LDB bridge"
depends on OF
+ depends on ARCH_MXC || COMPILE_TEST
select DRM_KMS_HELPER
select DRM_PANEL_BRIDGE
help
@@ -321,6 +322,22 @@ config DRM_TOSHIBA_TC358775
help
Toshiba TC358775 DSI/LVDS bridge chip driver.
+config DRM_TI_DLPC3433
+ tristate "TI DLPC3433 Display controller"
+ depends on DRM && DRM_PANEL
+ depends on OF
+ select DRM_MIPI_DSI
+ help
+ TI DLPC3433 is a MIPI DSI based display controller bridge
+ for processing high resolution DMD based projectors.
+
+ It has a flexible configuration of MIPI DSI and DPI signal
+ input that produces a DMD output in RGB565, RGB666, RGB888
+ formats.
+
+ It supports upto 720p resolution with 60 and 120 Hz refresh
+ rates.
+
config DRM_TI_TFP410
tristate "TI TFP410 DVI/HDMI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index f6c0a95de549..043b499545e3 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o
obj-$(CONFIG_DRM_TOSHIBA_TC358775) += tc358775.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
+obj-$(CONFIG_DRM_TI_DLPC3433) += ti-dlpc3433.o
obj-$(CONFIG_DRM_TI_SN65DSI83) += ti-sn65dsi83.o
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 9e3bb8a8ee40..a031a0cd1f18 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -226,18 +226,6 @@
#define ADV7511_REG_CEC_CLK_DIV 0x4e
#define ADV7511_REG_CEC_SOFT_RESET 0x50
-static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = {
- ADV7511_REG_CEC_RX1_FRAME_HDR,
- ADV7511_REG_CEC_RX2_FRAME_HDR,
- ADV7511_REG_CEC_RX3_FRAME_HDR,
-};
-
-static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = {
- ADV7511_REG_CEC_RX1_FRAME_LEN,
- ADV7511_REG_CEC_RX2_FRAME_LEN,
- ADV7511_REG_CEC_RX3_FRAME_LEN,
-};
-
#define ADV7533_REG_CEC_OFFSET 0x70
enum adv7511_input_clock {
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
index 399f625a50c8..0b266f28f150 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
@@ -15,6 +15,18 @@
#include "adv7511.h"
+static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = {
+ ADV7511_REG_CEC_RX1_FRAME_HDR,
+ ADV7511_REG_CEC_RX2_FRAME_HDR,
+ ADV7511_REG_CEC_RX3_FRAME_HDR,
+};
+
+static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = {
+ ADV7511_REG_CEC_RX1_FRAME_LEN,
+ ADV7511_REG_CEC_RX2_FRAME_LEN,
+ ADV7511_REG_CEC_RX3_FRAME_LEN,
+};
+
#define ADV7511_INT1_CEC_MASK \
(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 53a5da6c49dd..e92eb4a40745 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -1638,6 +1638,7 @@ static int anx7625_parse_dt(struct device *dev,
bus_type = 0;
mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes");
+ of_node_put(ep0);
}
if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */
diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index 829e1a144656..20bece84ff8c 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -462,6 +462,7 @@ struct cdns_dsi {
struct reset_control *dsi_p_rst;
struct clk *dsi_sys_clk;
bool link_initialized;
+ bool phy_initialized;
struct phy *dphy;
};
@@ -711,11 +712,21 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
pm_runtime_put(dsi->base.dev);
}
+static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+ struct cdns_dsi *dsi = input_to_dsi(input);
+
+ pm_runtime_put(dsi->base.dev);
+}
+
static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
{
struct cdns_dsi_output *output = &dsi->output;
u32 status;
+ if (dsi->phy_initialized)
+ return;
/*
* Power all internal DPHY blocks down and maintain their reset line
* asserted before changing the DPHY config.
@@ -739,6 +750,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
dsi->regs + MCTL_DPHY_CFG0);
+ dsi->phy_initialized = true;
}
static void cdns_dsi_init_link(struct cdns_dsi *dsi)
@@ -914,11 +926,25 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
writel(tmp, dsi->regs + MCTL_MAIN_EN);
}
+static void cdns_dsi_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
+ struct cdns_dsi *dsi = input_to_dsi(input);
+
+ if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0))
+ return;
+
+ cdns_dsi_init_link(dsi);
+ cdns_dsi_hs_init(dsi);
+}
+
static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = {
.attach = cdns_dsi_bridge_attach,
.mode_valid = cdns_dsi_bridge_mode_valid,
.disable = cdns_dsi_bridge_disable,
+ .pre_enable = cdns_dsi_bridge_pre_enable,
.enable = cdns_dsi_bridge_enable,
+ .post_disable = cdns_dsi_bridge_post_disable,
};
static int cdns_dsi_attach(struct mipi_dsi_host *host,
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index 47dea657a752..2b8cf64cbd80 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -9,6 +9,8 @@
#include <drm/drm_print.h>
#include <drm/drm_mipi_dsi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@@ -26,6 +28,11 @@
#define PD_CTRL(n) (0x0a + ((n) & 0x3)) /* 0..3 */
#define RST_CTRL(n) (0x0e + ((n) & 0x1)) /* 0..1 */
#define SYS_CTRL(n) (0x10 + ((n) & 0x7)) /* 0..4 */
+#define SYS_CTRL_1_CLK_PHASE_MSK GENMASK(5, 4)
+#define CLK_PHASE_0 0
+#define CLK_PHASE_1_4 1
+#define CLK_PHASE_1_2 2
+#define CLK_PHASE_3_4 3
#define RGB_DRV(n) (0x18 + ((n) & 0x3)) /* 0..3 */
#define RGB_DLY(n) (0x1c + ((n) & 0x1)) /* 0..1 */
#define RGB_TEST_CTRL 0x1e
@@ -100,7 +107,7 @@
#define MIPI_PN_SWAP 0x87
#define MIPI_PN_SWAP_CLK BIT(4)
#define MIPI_PN_SWAP_D(n) BIT((n) & 0x3)
-#define MIPI_SOT_SYNC_BIT_(n) (0x88 + ((n) & 0x1)) /* 0..1 */
+#define MIPI_SOT_SYNC_BIT(n) (0x88 + ((n) & 0x1)) /* 0..1 */
#define MIPI_ULPS_CTRL 0x8a
#define MIPI_CLK_CHK_VAR 0x8e
#define MIPI_CLK_CHK_INI 0x8f
@@ -115,7 +122,7 @@
#define MIPI_T_CLK_SETTLE 0x9a
#define MIPI_TO_HS_RX_L 0x9e
#define MIPI_TO_HS_RX_H 0x9f
-#define MIPI_PHY_(n) (0xa0 + ((n) & 0x7)) /* 0..5 */
+#define MIPI_PHY(n) (0xa0 + ((n) & 0x7)) /* 0..5 */
#define MIPI_PD_RX 0xb0
#define MIPI_PD_TERM 0xb1
#define MIPI_PD_HSRX 0xb2
@@ -125,13 +132,11 @@
#define MIPI_FORCE_0 0xb6
#define MIPI_RST_CTRL 0xb7
#define MIPI_RST_NUM 0xb8
-#define MIPI_DBG_SET_(n) (0xc0 + ((n) & 0xf)) /* 0..9 */
+#define MIPI_DBG_SET(n) (0xc0 + ((n) & 0xf)) /* 0..9 */
#define MIPI_DBG_SEL 0xe0
#define MIPI_DBG_DATA 0xe1
#define MIPI_ATE_TEST_SEL 0xe2
-#define MIPI_ATE_STATUS_(n) (0xe3 + ((n) & 0x1)) /* 0..1 */
-#define MIPI_ATE_STATUS_1 0xe4
-#define ICN6211_MAX_REGISTER MIPI_ATE_STATUS(1)
+#define MIPI_ATE_STATUS(n) (0xe3 + ((n) & 0x1)) /* 0..1 */
struct chipone {
struct device *dev;
@@ -155,10 +160,10 @@ static const struct regmap_range chipone_dsi_readable_ranges[] = {
regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE),
regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H),
regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE),
- regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)),
+ regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)),
regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM),
- regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)),
- regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)),
+ regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)),
+ regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)),
};
static const struct regmap_access_table chipone_dsi_readable_table = {
@@ -172,10 +177,10 @@ static const struct regmap_range chipone_dsi_writeable_ranges[] = {
regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE),
regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H),
regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE),
- regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)),
+ regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)),
regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM),
- regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)),
- regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)),
+ regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)),
+ regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)),
};
static const struct regmap_access_table chipone_dsi_writeable_table = {
@@ -189,7 +194,7 @@ static const struct regmap_config chipone_regmap_config = {
.rd_table = &chipone_dsi_readable_table,
.wr_table = &chipone_dsi_writeable_table,
.cache_type = REGCACHE_RBTREE,
- .max_register = MIPI_ATE_STATUS_(1),
+ .max_register = MIPI_ATE_STATUS(1),
};
static int chipone_dsi_read(void *context,
@@ -336,7 +341,7 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
const struct drm_bridge_state *bridge_state;
u16 hfp, hbp, hsync;
u32 bus_flags;
- u8 pol, id[4];
+ u8 pol, sys_ctrl_1, id[4];
chipone_readb(icn, VENDOR_ID, id);
chipone_readb(icn, DEVICE_ID_H, id + 1);
@@ -414,7 +419,14 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
chipone_configure_pll(icn, mode);
chipone_writeb(icn, SYS_CTRL(0), 0x40);
- chipone_writeb(icn, SYS_CTRL(1), 0x88);
+ sys_ctrl_1 = 0x88;
+
+ if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
+ sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_0);
+ else
+ sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_1_2);
+
+ chipone_writeb(icn, SYS_CTRL(1), sys_ctrl_1);
/* icn6211 specific sequence */
chipone_writeb(icn, MIPI_FORCE_0, 0x20);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index 7ef8fe5abc12..88f2a4f43cfb 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -578,15 +578,13 @@ static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode)
}
/* connector funcs */
-static enum drm_connector_status
-lt9611_connector_detect(struct drm_connector *connector, bool force)
+static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611)
{
- struct lt9611 *lt9611 = connector_to_lt9611(connector);
unsigned int reg_val = 0;
int connected = 0;
regmap_read(lt9611->regmap, 0x825e, &reg_val);
- connected = (reg_val & BIT(0));
+ connected = (reg_val & (BIT(2) | BIT(0)));
lt9611->status = connected ? connector_status_connected :
connector_status_disconnected;
@@ -594,6 +592,12 @@ lt9611_connector_detect(struct drm_connector *connector, bool force)
return lt9611->status;
}
+static enum drm_connector_status
+lt9611_connector_detect(struct drm_connector *connector, bool force)
+{
+ return __lt9611_detect(connector_to_lt9611(connector));
+}
+
static int lt9611_read_edid(struct lt9611 *lt9611)
{
unsigned int temp;
@@ -893,17 +897,7 @@ static void lt9611_bridge_mode_set(struct drm_bridge *bridge,
static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge)
{
- struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- unsigned int reg_val = 0;
- int connected;
-
- regmap_read(lt9611->regmap, 0x825e, &reg_val);
- connected = reg_val & BIT(0);
-
- lt9611->status = connected ? connector_status_connected :
- connector_status_disconnected;
-
- return lt9611->status;
+ return __lt9611_detect(bridge_to_lt9611(bridge));
}
static struct edid *lt9611_bridge_get_edid(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 3d62e6bf6892..310b3b194491 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -982,7 +982,7 @@ static int lt9611uxc_remove(struct i2c_client *client)
struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client);
disable_irq(client->irq);
- flush_scheduled_work();
+ cancel_work_sync(&lt9611uxc->work);
lt9611uxc_audio_exit(lt9611uxc);
drm_bridge_remove(&lt9611uxc->bridge);
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index ad74e6558eb3..d83111be4829 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -665,6 +665,12 @@ static int nwl_dsi_mode_set(struct nwl_dsi *dsi)
return ret;
}
+ ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set DSI phy mode: %d\n", ret);
+ goto uninit_phy;
+ }
+
ret = phy_configure(dsi->phy, phy_cfg);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret);
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index edb939b14c04..ff4227f6d800 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -537,12 +537,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = {
.pre_enable = ps8640_pre_enable,
};
-static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridge)
+static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge)
{
struct device_node *in_ep, *dsi_node;
struct mipi_dsi_device *dsi;
struct mipi_dsi_host *host;
- int ret;
const struct mipi_dsi_device_info info = { .type = "ps8640",
.channel = 0,
.node = NULL,
@@ -577,17 +576,40 @@ static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridg
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = NUM_MIPI_LANES;
- ret = devm_mipi_dsi_attach(dev, dsi);
+ return 0;
+}
+
+static int ps8640_bridge_link_panel(struct drm_dp_aux *aux)
+{
+ struct ps8640 *ps_bridge = aux_to_ps8640(aux);
+ struct device *dev = aux->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /*
+ * NOTE about returning -EPROBE_DEFER from this function: if we
+ * return an error (most relevant to -EPROBE_DEFER) it will only
+ * be passed out to ps8640_probe() if it called this directly (AKA the
+ * panel isn't under the "aux-bus" node). That should be fine because
+ * if the panel is under "aux-bus" it's guaranteed to have probed by
+ * the time this function has been called.
+ */
+
+ /* port@1 is ps8640 output port */
+ ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
+ if (IS_ERR(ps_bridge->panel_bridge))
+ return PTR_ERR(ps_bridge->panel_bridge);
+
+ ret = devm_drm_bridge_add(dev, &ps_bridge->bridge);
if (ret)
return ret;
- return 0;
+ return devm_mipi_dsi_attach(dev, ps_bridge->dsi);
}
static int ps8640_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
- struct device_node *np = dev->of_node;
struct ps8640 *ps_bridge;
int ret;
u32 i;
@@ -628,6 +650,14 @@ static int ps8640_probe(struct i2c_client *client)
if (!ps8640_of_panel_on_aux_bus(&client->dev))
ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
+ /*
+ * Get MIPI DSI resources early. These can return -EPROBE_DEFER so
+ * we want to get them out of the way sooner.
+ */
+ ret = ps8640_bridge_get_dsi_resources(&client->dev, ps_bridge);
+ if (ret)
+ return ret;
+
ps_bridge->page[PAGE0_DP_CNTL] = client;
ps_bridge->regmap[PAGE0_DP_CNTL] = devm_regmap_init_i2c(client, ps8640_regmap_config);
@@ -670,35 +700,19 @@ static int ps8640_probe(struct i2c_client *client)
if (ret)
return ret;
- devm_of_dp_aux_populate_ep_devices(&ps_bridge->aux);
-
- /* port@1 is ps8640 output port */
- ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
- if (IS_ERR(ps_bridge->panel_bridge))
- return PTR_ERR(ps_bridge->panel_bridge);
-
- drm_bridge_add(&ps_bridge->bridge);
-
- ret = ps8640_bridge_host_attach(dev, ps_bridge);
- if (ret)
- goto err_bridge_remove;
+ ret = devm_of_dp_aux_populate_bus(&ps_bridge->aux, ps8640_bridge_link_panel);
- return 0;
+ /*
+ * If devm_of_dp_aux_populate_bus() returns -ENODEV then it's up to
+ * usa to call ps8640_bridge_link_panel() directly. NOTE: in this case
+ * the function is allowed to -EPROBE_DEFER.
+ */
+ if (ret == -ENODEV)
+ return ps8640_bridge_link_panel(&ps_bridge->aux);
-err_bridge_remove:
- drm_bridge_remove(&ps_bridge->bridge);
return ret;
}
-static int ps8640_remove(struct i2c_client *client)
-{
- struct ps8640 *ps_bridge = i2c_get_clientdata(client);
-
- drm_bridge_remove(&ps_bridge->bridge);
-
- return 0;
-}
-
static const struct of_device_id ps8640_match[] = {
{ .compatible = "parade,ps8640" },
{ }
@@ -707,7 +721,6 @@ MODULE_DEVICE_TABLE(of, ps8640_match);
static struct i2c_driver ps8640_driver = {
.probe_new = ps8640_probe,
- .remove = ps8640_remove,
.driver = {
.name = "ps8640",
.of_match_table = ps8640_match,
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 485717c8f0b4..6c6177028742 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -3,10 +3,7 @@
* TC358767/TC358867/TC9595 DSI/DPI-to-DPI/(e)DP bridge driver
*
* The TC358767/TC358867/TC9595 can operate in multiple modes.
- * The following modes are supported:
- * DPI->(e)DP -- supported
- * DSI->DPI .... supported
- * DSI->(e)DP .. NOT supported
+ * All modes are supported -- DPI->(e)DP / DSI->DPI / DSI->(e)DP .
*
* Copyright (C) 2016 CogentEmbedded Inc
* Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
@@ -309,6 +306,9 @@ struct tc_data {
/* do we have IRQ */
bool have_irq;
+ /* Input connector type, DSI and not DPI. */
+ bool input_connector_dsi;
+
/* HPD pin number (0 or 1) or -ENODEV */
int hpd_pin;
};
@@ -1247,11 +1247,60 @@ static int tc_main_link_disable(struct tc_data *tc)
return regmap_write(tc->regmap, DP0CTL, 0);
}
-static int tc_dpi_stream_enable(struct tc_data *tc)
+static int tc_dsi_rx_enable(struct tc_data *tc)
{
+ u32 value;
int ret;
+
+ regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3);
+ regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3);
+ regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3);
+ regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3);
+ regmap_write(tc->regmap, PPI_D0S_ATMR, 0);
+ regmap_write(tc->regmap, PPI_D1S_ATMR, 0);
+ regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
+ regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD);
+
+ value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) |
+ LANEENABLE_CLEN;
+ regmap_write(tc->regmap, PPI_LANEENABLE, value);
+ regmap_write(tc->regmap, DSI_LANEENABLE, value);
+
+ /* Set input interface */
+ value = DP0_AUDSRC_NO_INPUT;
+ if (tc_test_pattern)
+ value |= DP0_VIDSRC_COLOR_BAR;
+ else
+ value |= DP0_VIDSRC_DSI_RX;
+ ret = regmap_write(tc->regmap, SYSCTRL, value);
+ if (ret)
+ return ret;
+
+ usleep_range(120, 150);
+
+ regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION);
+ regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START);
+
+ return 0;
+}
+
+static int tc_dpi_rx_enable(struct tc_data *tc)
+{
u32 value;
+ /* Set input interface */
+ value = DP0_AUDSRC_NO_INPUT;
+ if (tc_test_pattern)
+ value |= DP0_VIDSRC_COLOR_BAR;
+ else
+ value |= DP0_VIDSRC_DPI_RX;
+ return regmap_write(tc->regmap, SYSCTRL, value);
+}
+
+static int tc_dpi_stream_enable(struct tc_data *tc)
+{
+ int ret;
+
dev_dbg(tc->dev, "enable video stream\n");
/* Setup PLL */
@@ -1277,20 +1326,6 @@ static int tc_dpi_stream_enable(struct tc_data *tc)
if (ret)
return ret;
- regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3);
- regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3);
- regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3);
- regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3);
- regmap_write(tc->regmap, PPI_D0S_ATMR, 0);
- regmap_write(tc->regmap, PPI_D1S_ATMR, 0);
- regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
- regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD);
-
- value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) |
- LANEENABLE_CLEN;
- regmap_write(tc->regmap, PPI_LANEENABLE, value);
- regmap_write(tc->regmap, DSI_LANEENABLE, value);
-
ret = tc_set_common_video_mode(tc, &tc->mode);
if (ret)
return ret;
@@ -1299,22 +1334,7 @@ static int tc_dpi_stream_enable(struct tc_data *tc)
if (ret)
return ret;
- /* Set input interface */
- value = DP0_AUDSRC_NO_INPUT;
- if (tc_test_pattern)
- value |= DP0_VIDSRC_COLOR_BAR;
- else
- value |= DP0_VIDSRC_DSI_RX;
- ret = regmap_write(tc->regmap, SYSCTRL, value);
- if (ret)
- return ret;
-
- usleep_range(120, 150);
-
- regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION);
- regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START);
-
- return 0;
+ return tc_dsi_rx_enable(tc);
}
static int tc_dpi_stream_disable(struct tc_data *tc)
@@ -1333,8 +1353,18 @@ static int tc_edp_stream_enable(struct tc_data *tc)
dev_dbg(tc->dev, "enable video stream\n");
- /* PXL PLL setup */
- if (tc_test_pattern) {
+ /*
+ * Pixel PLL must be enabled for DSI input mode and test pattern.
+ *
+ * Per TC9595XBG datasheet Revision 0.1 2018-12-27 Figure 4.18
+ * "Clock Mode Selection and Clock Sources", either Pixel PLL
+ * or DPI_PCLK supplies StrmClk. DPI_PCLK is only available in
+ * case valid Pixel Clock are supplied to the chip DPI input.
+ * In case built-in test pattern is desired OR DSI input mode
+ * is used, DPI_PCLK is not available and thus Pixel PLL must
+ * be used instead.
+ */
+ if (tc->input_connector_dsi || tc_test_pattern) {
ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
1000 * tc->mode.clock);
if (ret)
@@ -1372,17 +1402,12 @@ static int tc_edp_stream_enable(struct tc_data *tc)
ret = regmap_write(tc->regmap, DP0CTL, value);
if (ret)
return ret;
+
/* Set input interface */
- value = DP0_AUDSRC_NO_INPUT;
- if (tc_test_pattern)
- value |= DP0_VIDSRC_COLOR_BAR;
+ if (tc->input_connector_dsi)
+ return tc_dsi_rx_enable(tc);
else
- value |= DP0_VIDSRC_DPI_RX;
- ret = regmap_write(tc->regmap, SYSCTRL, value);
- if (ret)
- return ret;
-
- return 0;
+ return tc_dpi_rx_enable(tc);
}
static int tc_edp_stream_disable(struct tc_data *tc)
@@ -1871,7 +1896,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc)
of_node_put(host_node);
of_node_put(endpoint);
- if (dsi_lanes < 0 || dsi_lanes > 4)
+ if (dsi_lanes <= 0 || dsi_lanes > 4)
return -EINVAL;
if (!host)
@@ -1992,18 +2017,29 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc)
mode |= BIT(endpoint.port);
}
- if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp)
+ if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) {
+ tc->input_connector_dsi = false;
return tc_probe_edp_bridge_endpoint(tc);
- else if (mode == mode_dsi_to_dpi)
+ } else if (mode == mode_dsi_to_dpi) {
+ tc->input_connector_dsi = true;
return tc_probe_dpi_bridge_endpoint(tc);
- else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp)
- dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n");
- else
- dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode);
+ } else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp) {
+ tc->input_connector_dsi = true;
+ return tc_probe_edp_bridge_endpoint(tc);
+ }
+
+ dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode);
return -EINVAL;
}
+static void tc_clk_disable(void *data)
+{
+ struct clk *refclk = data;
+
+ clk_disable_unprepare(refclk);
+}
+
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
@@ -2020,6 +2056,24 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (ret)
return ret;
+ tc->refclk = devm_clk_get(dev, "ref");
+ if (IS_ERR(tc->refclk)) {
+ ret = PTR_ERR(tc->refclk);
+ dev_err(dev, "Failed to get refclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(tc->refclk);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk);
+ if (ret)
+ return ret;
+
+ /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */
+ usleep_range(10, 15);
+
/* Shut down GPIO is optional */
tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
if (IS_ERR(tc->sd_gpio))
@@ -2040,13 +2094,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
usleep_range(5000, 10000);
}
- tc->refclk = devm_clk_get(dev, "ref");
- if (IS_ERR(tc->refclk)) {
- ret = PTR_ERR(tc->refclk);
- dev_err(dev, "Failed to get refclk: %d\n", ret);
- return ret;
- }
-
tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config);
if (IS_ERR(tc->regmap)) {
ret = PTR_ERR(tc->regmap);
@@ -2137,7 +2184,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, tc);
- if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) { /* DPI output */
+ if (tc->input_connector_dsi) { /* DSI input */
ret = tc_mipi_dsi_host_attach(tc);
if (ret) {
drm_bridge_remove(&tc->bridge);
diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c
new file mode 100644
index 000000000000..06e519798ac5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 RenewOutReach
+ * Copyright (C) 2021 Amarula Solutions(India)
+ *
+ * Author:
+ * Jagan Teki <jagan@amarulasolutions.com>
+ * Christopher Vollo <chris@renewoutreach.org>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_mipi_dsi.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+enum cmd_registers {
+ WR_INPUT_SOURCE = 0x05, /* Write Input Source Select */
+ WR_EXT_SOURCE_FMT = 0x07, /* Write External Video Source Format */
+ WR_IMAGE_CROP = 0x10, /* Write Image Crop */
+ WR_DISPLAY_SIZE = 0x12, /* Write Display Size */
+ WR_IMAGE_FREEZE = 0x1A, /* Write Image Freeze */
+ WR_INPUT_IMAGE_SIZE = 0x2E, /* Write External Input Image Size */
+ WR_RGB_LED_EN = 0x52, /* Write RGB LED Enable */
+ WR_RGB_LED_CURRENT = 0x54, /* Write RGB LED Current */
+ WR_RGB_LED_MAX_CURRENT = 0x5C, /* Write RGB LED Max Current */
+ WR_DSI_HS_CLK = 0xBD, /* Write DSI HS Clock */
+ RD_DEVICE_ID = 0xD4, /* Read Controller Device ID */
+ WR_DSI_PORT_EN = 0xD7, /* Write DSI Port Enable */
+};
+
+enum input_source {
+ INPUT_EXTERNAL_VIDEO = 0,
+ INPUT_TEST_PATTERN,
+ INPUT_SPLASH_SCREEN,
+};
+
+#define DEV_ID_MASK GENMASK(3, 0)
+#define IMAGE_FREESE_EN BIT(0)
+#define DSI_PORT_EN 0
+#define EXT_SOURCE_FMT_DSI 0
+#define RED_LED_EN BIT(0)
+#define GREEN_LED_EN BIT(1)
+#define BLUE_LED_EN BIT(2)
+#define LED_MASK GENMASK(2, 0)
+#define MAX_BYTE_SIZE 8
+
+struct dlpc {
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct device_node *host_node;
+ struct mipi_dsi_device *dsi;
+ struct drm_display_mode mode;
+
+ struct gpio_desc *enable_gpio;
+ struct regulator *vcc_intf;
+ struct regulator *vcc_flsh;
+ struct regmap *regmap;
+ unsigned int dsi_lanes;
+};
+
+static inline struct dlpc *bridge_to_dlpc(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct dlpc, bridge);
+}
+
+static bool dlpc_writeable_noinc_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WR_IMAGE_CROP:
+ case WR_DISPLAY_SIZE:
+ case WR_INPUT_IMAGE_SIZE:
+ case WR_DSI_HS_CLK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range dlpc_volatile_ranges[] = {
+ { .range_min = 0x10, .range_max = 0xBF },
+};
+
+static const struct regmap_access_table dlpc_volatile_table = {
+ .yes_ranges = dlpc_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges),
+};
+
+static struct regmap_config dlpc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = WR_DSI_PORT_EN,
+ .writeable_noinc_reg = dlpc_writeable_noinc_reg,
+ .volatile_table = &dlpc_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+ .name = "dlpc3433",
+};
+
+static void dlpc_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct dlpc *dlpc = bridge_to_dlpc(bridge);
+ struct device *dev = dlpc->dev;
+ struct drm_display_mode *mode = &dlpc->mode;
+ struct regmap *regmap = dlpc->regmap;
+ char buf[MAX_BYTE_SIZE];
+ unsigned int devid;
+
+ regmap_read(regmap, RD_DEVICE_ID, &devid);
+ devid &= DEV_ID_MASK;
+
+ DRM_DEV_DEBUG(dev, "DLPC3433 device id: 0x%02x\n", devid);
+
+ if (devid != 0x01) {
+ DRM_DEV_ERROR(dev, "Unsupported DLPC device id: 0x%02x\n", devid);
+ return;
+ }
+
+ /* disable image freeze */
+ regmap_write(regmap, WR_IMAGE_FREEZE, IMAGE_FREESE_EN);
+
+ /* enable DSI port */
+ regmap_write(regmap, WR_DSI_PORT_EN, DSI_PORT_EN);
+
+ memset(buf, 0, MAX_BYTE_SIZE);
+
+ /* set image crop */
+ buf[4] = mode->hdisplay & 0xff;
+ buf[5] = (mode->hdisplay & 0xff00) >> 8;
+ buf[6] = mode->vdisplay & 0xff;
+ buf[7] = (mode->vdisplay & 0xff00) >> 8;
+ regmap_noinc_write(regmap, WR_IMAGE_CROP, buf, MAX_BYTE_SIZE);
+
+ /* set display size */
+ buf[4] = mode->hdisplay & 0xff;
+ buf[5] = (mode->hdisplay & 0xff00) >> 8;
+ buf[6] = mode->vdisplay & 0xff;
+ buf[7] = (mode->vdisplay & 0xff00) >> 8;
+ regmap_noinc_write(regmap, WR_DISPLAY_SIZE, buf, MAX_BYTE_SIZE);
+
+ /* set input image size */
+ buf[0] = mode->hdisplay & 0xff;
+ buf[1] = (mode->hdisplay & 0xff00) >> 8;
+ buf[2] = mode->vdisplay & 0xff;
+ buf[3] = (mode->vdisplay & 0xff00) >> 8;
+ regmap_noinc_write(regmap, WR_INPUT_IMAGE_SIZE, buf, 4);
+
+ /* set external video port */
+ regmap_write(regmap, WR_INPUT_SOURCE, INPUT_EXTERNAL_VIDEO);
+
+ /* set external video format select as DSI */
+ regmap_write(regmap, WR_EXT_SOURCE_FMT, EXT_SOURCE_FMT_DSI);
+
+ /* disable image freeze */
+ regmap_write(regmap, WR_IMAGE_FREEZE, 0x00);
+
+ /* enable RGB led */
+ regmap_update_bits(regmap, WR_RGB_LED_EN, LED_MASK,
+ RED_LED_EN | GREEN_LED_EN | BLUE_LED_EN);
+
+ msleep(10);
+}
+
+static void dlpc_atomic_pre_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct dlpc *dlpc = bridge_to_dlpc(bridge);
+ int ret;
+
+ gpiod_set_value(dlpc->enable_gpio, 1);
+
+ msleep(500);
+
+ ret = regulator_enable(dlpc->vcc_intf);
+ if (ret)
+ DRM_DEV_ERROR(dlpc->dev,
+ "failed to enable VCC_INTF regulator: %d\n", ret);
+
+ ret = regulator_enable(dlpc->vcc_flsh);
+ if (ret)
+ DRM_DEV_ERROR(dlpc->dev,
+ "failed to enable VCC_FLSH regulator: %d\n", ret);
+
+ msleep(10);
+}
+
+static void dlpc_atomic_post_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct dlpc *dlpc = bridge_to_dlpc(bridge);
+
+ regulator_disable(dlpc->vcc_flsh);
+ regulator_disable(dlpc->vcc_intf);
+
+ msleep(10);
+
+ gpiod_set_value(dlpc->enable_gpio, 0);
+
+ msleep(500);
+}
+
+#define MAX_INPUT_SEL_FORMATS 1
+
+static u32 *
+dlpc_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;
+
+ *num_input_fmts = 0;
+
+ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ /* This is the DSI-end bus format */
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ *num_input_fmts = 1;
+
+ return input_fmts;
+}
+
+static void dlpc_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct dlpc *dlpc = bridge_to_dlpc(bridge);
+
+ drm_mode_copy(&dlpc->mode, adjusted_mode);
+}
+
+static int dlpc_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct dlpc *dlpc = bridge_to_dlpc(bridge);
+
+ return drm_bridge_attach(bridge->encoder, dlpc->next_bridge, bridge, flags);
+}
+
+static const struct drm_bridge_funcs dlpc_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_pre_enable = dlpc_atomic_pre_enable,
+ .atomic_enable = dlpc_atomic_enable,
+ .atomic_post_disable = dlpc_atomic_post_disable,
+ .mode_set = dlpc_mode_set,
+ .attach = dlpc_attach,
+};
+
+static int dlpc3433_parse_dt(struct dlpc *dlpc)
+{
+ struct device *dev = dlpc->dev;
+ struct device_node *endpoint;
+ int ret;
+
+ dlpc->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(dlpc->enable_gpio))
+ return PTR_ERR(dlpc->enable_gpio);
+
+ dlpc->vcc_intf = devm_regulator_get(dlpc->dev, "vcc_intf");
+ if (IS_ERR(dlpc->vcc_intf))
+ return dev_err_probe(dev, PTR_ERR(dlpc->vcc_intf),
+ "failed to get VCC_INTF supply\n");
+
+ dlpc->vcc_flsh = devm_regulator_get(dlpc->dev, "vcc_flsh");
+ if (IS_ERR(dlpc->vcc_flsh))
+ return dev_err_probe(dev, PTR_ERR(dlpc->vcc_flsh),
+ "failed to get VCC_FLSH supply\n");
+
+ dlpc->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(dlpc->next_bridge))
+ return PTR_ERR(dlpc->next_bridge);
+
+ endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
+ dlpc->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
+ if (dlpc->dsi_lanes < 0 || dlpc->dsi_lanes > 4) {
+ ret = -EINVAL;
+ goto err_put_endpoint;
+ }
+
+ dlpc->host_node = of_graph_get_remote_port_parent(endpoint);
+ if (!dlpc->host_node) {
+ ret = -ENODEV;
+ goto err_put_host;
+ }
+
+ of_node_put(endpoint);
+
+ return 0;
+
+err_put_host:
+ of_node_put(dlpc->host_node);
+err_put_endpoint:
+ of_node_put(endpoint);
+ return ret;
+}
+
+static int dlpc_host_attach(struct dlpc *dlpc)
+{
+ struct device *dev = dlpc->dev;
+ struct mipi_dsi_host *host;
+ struct mipi_dsi_device_info info = {
+ .type = "dlpc3433",
+ .channel = 0,
+ .node = NULL,
+ };
+
+ host = of_find_mipi_dsi_host_by_node(dlpc->host_node);
+ if (!host) {
+ DRM_DEV_ERROR(dev, "failed to find dsi host\n");
+ return -EPROBE_DEFER;
+ }
+
+ dlpc->dsi = mipi_dsi_device_register_full(host, &info);
+ if (IS_ERR(dlpc->dsi)) {
+ DRM_DEV_ERROR(dev, "failed to create dsi device\n");
+ return PTR_ERR(dlpc->dsi);
+ }
+
+ dlpc->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
+ dlpc->dsi->format = MIPI_DSI_FMT_RGB565;
+ dlpc->dsi->lanes = dlpc->dsi_lanes;
+
+ return devm_mipi_dsi_attach(dev, dlpc->dsi);
+}
+
+static int dlpc3433_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct dlpc *dlpc;
+ int ret;
+
+ dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL);
+ if (!dlpc)
+ return -ENOMEM;
+
+ dlpc->dev = dev;
+
+ dlpc->regmap = devm_regmap_init_i2c(client, &dlpc_regmap_config);
+ if (IS_ERR(dlpc->regmap))
+ return PTR_ERR(dlpc->regmap);
+
+ ret = dlpc3433_parse_dt(dlpc);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, dlpc);
+ i2c_set_clientdata(client, dlpc);
+
+ dlpc->bridge.funcs = &dlpc_bridge_funcs;
+ dlpc->bridge.of_node = dev->of_node;
+ drm_bridge_add(&dlpc->bridge);
+
+ ret = dlpc_host_attach(dlpc);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to attach dsi host\n");
+ goto err_remove_bridge;
+ }
+
+ return 0;
+
+err_remove_bridge:
+ drm_bridge_remove(&dlpc->bridge);
+ return ret;
+}
+
+static int dlpc3433_remove(struct i2c_client *client)
+{
+ struct dlpc *dlpc = i2c_get_clientdata(client);
+
+ drm_bridge_remove(&dlpc->bridge);
+ of_node_put(dlpc->host_node);
+
+ return 0;
+}
+
+static const struct i2c_device_id dlpc3433_id[] = {
+ { "ti,dlpc3433", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, dlpc3433_id);
+
+static const struct of_device_id dlpc3433_match_table[] = {
+ { .compatible = "ti,dlpc3433" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dlpc3433_match_table);
+
+static struct i2c_driver dlpc3433_driver = {
+ .probe_new = dlpc3433_probe,
+ .remove = dlpc3433_remove,
+ .id_table = dlpc3433_id,
+ .driver = {
+ .name = "ti-dlpc3433",
+ .of_match_table = dlpc3433_match_table,
+ },
+};
+module_i2c_driver(dlpc3433_driver);
+
+MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
+MODULE_AUTHOR("Christopher Vollo <chris@renewoutreach.org>");
+MODULE_DESCRIPTION("TI DLPC3433 MIPI DSI Display Controller Bridge");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/display/drm_dp_aux_bus.c b/drivers/gpu/drm/display/drm_dp_aux_bus.c
index dccf3e2ea323..f5741b45ca07 100644
--- a/drivers/gpu/drm/display/drm_dp_aux_bus.c
+++ b/drivers/gpu/drm/display/drm_dp_aux_bus.c
@@ -3,10 +3,10 @@
* Copyright 2021 Google Inc.
*
* The DP AUX bus is used for devices that are connected over a DisplayPort
- * AUX bus. The devices on the far side of the bus are referred to as
- * endpoints in this code.
+ * AUX bus. The device on the far side of the bus is referred to as an
+ * endpoint in this code.
*
- * Commonly there is only one device connected to the DP AUX bus: a panel.
+ * There is only one device connected to the DP AUX bus: an eDP panel.
* Though historically panels (even DP panels) have been modeled as simple
* platform devices, putting them under the DP AUX bus allows the panel driver
* to perform transactions on that bus.
@@ -22,6 +22,11 @@
#include <drm/display/drm_dp_aux_bus.h>
#include <drm/display/drm_dp_helper.h>
+struct dp_aux_ep_device_with_data {
+ struct dp_aux_ep_device aux_ep;
+ int (*done_probing)(struct drm_dp_aux *aux);
+};
+
/**
* dp_aux_ep_match() - The match function for the dp_aux_bus.
* @dev: The device to match.
@@ -48,6 +53,8 @@ static int dp_aux_ep_probe(struct device *dev)
{
struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
+ struct dp_aux_ep_device_with_data *aux_ep_with_data =
+ container_of(aux_ep, struct dp_aux_ep_device_with_data, aux_ep);
int ret;
ret = dev_pm_domain_attach(dev, true);
@@ -56,7 +63,32 @@ static int dp_aux_ep_probe(struct device *dev)
ret = aux_ep_drv->probe(aux_ep);
if (ret)
- dev_pm_domain_detach(dev, true);
+ goto err_attached;
+
+ if (aux_ep_with_data->done_probing) {
+ ret = aux_ep_with_data->done_probing(aux_ep->aux);
+ if (ret) {
+ /*
+ * The done_probing() callback should not return
+ * -EPROBE_DEFER to us. If it does, we treat it as an
+ * error. Passing it on as-is would cause the _panel_
+ * to defer.
+ */
+ if (ret == -EPROBE_DEFER) {
+ dev_err(dev,
+ "DP AUX done_probing() can't defer\n");
+ ret = -EINVAL;
+ }
+ goto err_probed;
+ }
+ }
+
+ return 0;
+err_probed:
+ if (aux_ep_drv->remove)
+ aux_ep_drv->remove(aux_ep);
+err_attached:
+ dev_pm_domain_detach(dev, true);
return ret;
}
@@ -66,7 +98,6 @@ static int dp_aux_ep_probe(struct device *dev)
* @dev: The device to remove.
*
* Calls through to the endpoint driver remove.
- *
*/
static void dp_aux_ep_remove(struct device *dev)
{
@@ -120,12 +151,14 @@ ATTRIBUTE_GROUPS(dp_aux_ep_dev);
/**
* dp_aux_ep_dev_release() - Free memory for the dp_aux_ep device
* @dev: The device to free.
- *
- * Return: 0 if no error or negative error code.
*/
static void dp_aux_ep_dev_release(struct device *dev)
{
- kfree(to_dp_aux_ep_dev(dev));
+ struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
+ struct dp_aux_ep_device_with_data *aux_ep_with_data =
+ container_of(aux_ep, struct dp_aux_ep_device_with_data, aux_ep);
+
+ kfree(aux_ep_with_data);
}
static struct device_type dp_aux_device_type_type = {
@@ -139,12 +172,14 @@ static struct device_type dp_aux_device_type_type = {
* @dev: The device to destroy.
* @data: Not used
*
- * This is just used as a callback by of_dp_aux_depopulate_ep_devices() and
+ * This is just used as a callback by of_dp_aux_depopulate_bus() and
* is called for _all_ of the child devices of the device providing the AUX bus.
* We'll only act on those that are of type "dp_aux_bus_type".
*
- * This function is effectively an inverse of what's in the loop
- * in of_dp_aux_populate_ep_devices().
+ * This function is effectively an inverse of what's in
+ * of_dp_aux_populate_bus(). NOTE: since we only populate one child
+ * then it's expected that only one device will match all the "if" tests in
+ * this function and get to the device_unregister().
*
* Return: 0 if no error or negative error code.
*/
@@ -167,122 +202,150 @@ static int of_dp_aux_ep_destroy(struct device *dev, void *data)
}
/**
- * of_dp_aux_depopulate_ep_devices() - Undo of_dp_aux_populate_ep_devices
- * @aux: The AUX channel whose devices we want to depopulate
+ * of_dp_aux_depopulate_bus() - Undo of_dp_aux_populate_bus
+ * @aux: The AUX channel whose device we want to depopulate
*
- * This will destroy all devices that were created
- * by of_dp_aux_populate_ep_devices().
+ * This will destroy the device that was created
+ * by of_dp_aux_populate_bus().
*/
-void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux)
+void of_dp_aux_depopulate_bus(struct drm_dp_aux *aux)
{
device_for_each_child_reverse(aux->dev, NULL, of_dp_aux_ep_destroy);
}
-EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_ep_devices);
+EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_bus);
/**
- * of_dp_aux_populate_ep_devices() - Populate the endpoint devices on the DP AUX
- * @aux: The AUX channel whose devices we want to populate. It is required that
+ * of_dp_aux_populate_bus() - Populate the endpoint device on the DP AUX
+ * @aux: The AUX channel whose device we want to populate. It is required that
* drm_dp_aux_init() has already been called for this AUX channel.
+ * @done_probing: Callback functions to call after EP device finishes probing.
+ * Will not be called if there are no EP devices and this
+ * function will return -ENODEV.
*
- * This will populate all the devices under the "aux-bus" node of the device
- * providing the AUX channel (AKA aux->dev).
+ * This will populate the device (expected to be an eDP panel) under the
+ * "aux-bus" node of the device providing the AUX channel (AKA aux->dev).
*
* When this function finishes, it is _possible_ (but not guaranteed) that
- * our sub-devices will have finished probing. It should be noted that if our
- * sub-devices return -EPROBE_DEFER that we will not return any error codes
- * ourselves but our sub-devices will _not_ have actually probed successfully
- * yet. There may be other cases (maybe added in the future?) where sub-devices
- * won't have been probed yet when this function returns, so it's best not to
- * rely on that.
+ * our sub-device will have finished probing. It should be noted that if our
+ * sub-device returns -EPROBE_DEFER or is probing asynchronously for some
+ * reason that we will not return any error codes ourselves but our
+ * sub-device will _not_ have actually probed successfully yet.
+ *
+ * In many cases it's important for the caller of this function to be notified
+ * when our sub device finishes probing. Our sub device is expected to be an
+ * eDP panel and the caller is expected to be an eDP controller. The eDP
+ * controller needs to be able to get a reference to the panel when it finishes
+ * probing. For this reason the caller can pass in a function pointer that
+ * will be called when our sub-device finishes probing.
*
* If this function succeeds you should later make sure you call
- * of_dp_aux_depopulate_ep_devices() to undo it, or just use the devm version
+ * of_dp_aux_depopulate_bus() to undo it, or just use the devm version
* of this function.
*
- * Return: 0 if no error or negative error code.
+ * Return: 0 if no error or negative error code; returns -ENODEV if there are
+ * no children. The done_probing() function won't be called in that
+ * case.
*/
-int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+int of_dp_aux_populate_bus(struct drm_dp_aux *aux,
+ int (*done_probing)(struct drm_dp_aux *aux))
{
- struct device_node *bus, *np;
+ struct device_node *bus = NULL, *np = NULL;
struct dp_aux_ep_device *aux_ep;
+ struct dp_aux_ep_device_with_data *aux_ep_with_data;
int ret;
/* drm_dp_aux_init() should have been called already; warn if not */
WARN_ON_ONCE(!aux->ddc.algo);
if (!aux->dev->of_node)
- return 0;
-
+ return -ENODEV;
bus = of_get_child_by_name(aux->dev->of_node, "aux-bus");
if (!bus)
- return 0;
+ return -ENODEV;
- for_each_available_child_of_node(bus, np) {
- if (of_node_test_and_set_flag(np, OF_POPULATED))
- continue;
+ np = of_get_next_available_child(bus, NULL);
+ of_node_put(bus);
+ if (!np)
+ return -ENODEV;
- aux_ep = kzalloc(sizeof(*aux_ep), GFP_KERNEL);
- if (!aux_ep)
- continue;
- aux_ep->aux = aux;
+ if (of_node_test_and_set_flag(np, OF_POPULATED)) {
+ dev_err(aux->dev, "DP AUX EP device already populated\n");
+ ret = -EINVAL;
+ goto err_did_get_np;
+ }
- aux_ep->dev.parent = aux->dev;
- aux_ep->dev.bus = &dp_aux_bus_type;
- aux_ep->dev.type = &dp_aux_device_type_type;
- aux_ep->dev.of_node = of_node_get(np);
- dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev));
+ aux_ep_with_data = kzalloc(sizeof(*aux_ep_with_data), GFP_KERNEL);
+ if (!aux_ep_with_data) {
+ ret = -ENOMEM;
+ goto err_did_set_populated;
+ }
- ret = device_register(&aux_ep->dev);
- if (ret) {
- dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret);
- of_node_clear_flag(np, OF_POPULATED);
- of_node_put(np);
+ aux_ep_with_data->done_probing = done_probing;
- /*
- * As per docs of device_register(), call this instead
- * of kfree() directly for error cases.
- */
- put_device(&aux_ep->dev);
+ aux_ep = &aux_ep_with_data->aux_ep;
+ aux_ep->aux = aux;
+ aux_ep->dev.parent = aux->dev;
+ aux_ep->dev.bus = &dp_aux_bus_type;
+ aux_ep->dev.type = &dp_aux_device_type_type;
+ aux_ep->dev.of_node = of_node_get(np);
+ dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev));
- /*
- * Following in the footsteps of of_i2c_register_devices(),
- * we won't fail the whole function here--we'll just
- * continue registering any other devices we find.
- */
- }
- }
+ ret = device_register(&aux_ep->dev);
+ if (ret) {
+ dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret);
- of_node_put(bus);
+ /*
+ * As per docs of device_register(), call this instead
+ * of kfree() directly for error cases.
+ */
+ put_device(&aux_ep->dev);
+
+ goto err_did_set_populated;
+ }
return 0;
+
+err_did_set_populated:
+ of_node_clear_flag(np, OF_POPULATED);
+
+err_did_get_np:
+ of_node_put(np);
+
+ return ret;
}
+EXPORT_SYMBOL_GPL(of_dp_aux_populate_bus);
-static void of_dp_aux_depopulate_ep_devices_void(void *data)
+static void of_dp_aux_depopulate_bus_void(void *data)
{
- of_dp_aux_depopulate_ep_devices(data);
+ of_dp_aux_depopulate_bus(data);
}
/**
- * devm_of_dp_aux_populate_ep_devices() - devm wrapper for of_dp_aux_populate_ep_devices()
- * @aux: The AUX channel whose devices we want to populate
+ * devm_of_dp_aux_populate_bus() - devm wrapper for of_dp_aux_populate_bus()
+ * @aux: The AUX channel whose device we want to populate
+ * @done_probing: Callback functions to call after EP device finishes probing.
+ * Will not be called if there are no EP devices and this
+ * function will return -ENODEV.
*
* Handles freeing w/ devm on the device "aux->dev".
*
- * Return: 0 if no error or negative error code.
+ * Return: 0 if no error or negative error code; returns -ENODEV if there are
+ * no children. The done_probing() function won't be called in that
+ * case.
*/
-int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+int devm_of_dp_aux_populate_bus(struct drm_dp_aux *aux,
+ int (*done_probing)(struct drm_dp_aux *aux))
{
int ret;
- ret = of_dp_aux_populate_ep_devices(aux);
+ ret = of_dp_aux_populate_bus(aux, done_probing);
if (ret)
return ret;
return devm_add_action_or_reset(aux->dev,
- of_dp_aux_depopulate_ep_devices_void,
- aux);
+ of_dp_aux_depopulate_bus_void, aux);
}
-EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_ep_devices);
+EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_bus);
int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *drv, struct module *owner)
{
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c96847fc0ebc..e275b4ca344b 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -170,6 +170,29 @@ void drm_bridge_add(struct drm_bridge *bridge)
}
EXPORT_SYMBOL(drm_bridge_add);
+static void drm_bridge_remove_void(void *bridge)
+{
+ drm_bridge_remove(bridge);
+}
+
+/**
+ * devm_drm_bridge_add - devm managed version of drm_bridge_add()
+ *
+ * @dev: device to tie the bridge lifetime to
+ * @bridge: bridge control structure
+ *
+ * This is the managed version of drm_bridge_add() which automatically
+ * calls drm_bridge_remove() when @dev is unbound.
+ *
+ * Return: 0 if no error or negative error code.
+ */
+int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge)
+{
+ drm_bridge_add(bridge);
+ return devm_add_action_or_reset(dev, drm_bridge_remove_void, bridge);
+}
+EXPORT_SYMBOL(devm_drm_bridge_add);
+
/**
* drm_bridge_remove - remove the given bridge from the global bridge list
*
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
index e6346a67cd98..48e6ce16439f 100644
--- a/drivers/gpu/drm/drm_client_modeset.c
+++ b/drivers/gpu/drm/drm_client_modeset.c
@@ -158,22 +158,31 @@ drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int
return NULL;
}
-static struct drm_display_mode *
-drm_connector_pick_cmdline_mode(struct drm_connector *connector)
+static struct drm_display_mode *drm_connector_pick_cmdline_mode(struct drm_connector *connector)
{
struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode;
bool prefer_non_interlace;
+ /*
+ * Find a user-defined mode. If the user gave us a valid
+ * mode on the kernel command line, it will show up in this
+ * list.
+ */
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (mode->type & DRM_MODE_TYPE_USERDEF)
+ return mode;
+ }
+
cmdline_mode = &connector->cmdline_mode;
if (cmdline_mode->specified == false)
return NULL;
- /* attempt to find a matching mode in the list of modes
- * we have gotten so far, if not add a CVT mode that conforms
+ /*
+ * Attempt to find a matching mode in the list of modes we
+ * have gotten so far.
*/
- if (cmdline_mode->rb || cmdline_mode->margins)
- goto create_mode;
prefer_non_interlace = !cmdline_mode->interlace;
again:
@@ -207,12 +216,7 @@ again:
goto again;
}
-create_mode:
- mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode);
- if (mode)
- list_add(&mode->head, &connector->modes);
-
- return mode;
+ return NULL;
}
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 7f1b82dbaebb..fb04b7a984de 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -395,6 +395,23 @@ static int vrr_range_show(struct seq_file *m, void *data)
}
DEFINE_SHOW_ATTRIBUTE(vrr_range);
+/*
+ * Returns Connector's max supported bpc through debugfs file.
+ * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc
+ */
+static int output_bpc_show(struct seq_file *m, void *data)
+{
+ struct drm_connector *connector = m->private;
+
+ if (connector->status != connector_status_connected)
+ return -ENODEV;
+
+ seq_printf(m, "Maximum: %u\n", connector->display_info.bpc);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(output_bpc);
+
static const struct file_operations drm_edid_fops = {
.owner = THIS_MODULE,
.open = edid_open,
@@ -437,6 +454,10 @@ void drm_debugfs_connector_add(struct drm_connector *connector)
debugfs_create_file("vrr_range", S_IRUGO, root, connector,
&vrr_range_fops);
+ /* max bpc */
+ debugfs_create_file("output_bpc", 0444, root, connector,
+ &output_bpc_fops);
+
if (connector->funcs->debugfs_init)
connector->funcs->debugfs_init(connector, root);
}
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
index 32da557b960f..38ea8203df45 100644
--- a/drivers/gpu/drm/drm_displayid.c
+++ b/drivers/gpu/drm/drm_displayid.c
@@ -33,11 +33,11 @@ static int validate_displayid(const u8 *displayid, int length, int idx)
return 0;
}
-static const u8 *drm_find_displayid_extension(const struct edid *edid,
+static const u8 *drm_find_displayid_extension(const struct drm_edid *drm_edid,
int *length, int *idx,
int *ext_index)
{
- const u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index);
+ const u8 *displayid = drm_find_edid_extension(drm_edid, DISPLAYID_EXT, ext_index);
const struct displayid_header *base;
int ret;
@@ -58,12 +58,12 @@ static const u8 *drm_find_displayid_extension(const struct edid *edid,
return displayid;
}
-void displayid_iter_edid_begin(const struct edid *edid,
+void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
struct displayid_iter *iter)
{
memset(iter, 0, sizeof(*iter));
- iter->edid = edid;
+ iter->drm_edid = drm_edid;
}
static const struct displayid_block *
@@ -88,7 +88,7 @@ __displayid_iter_next(struct displayid_iter *iter)
{
const struct displayid_block *block;
- if (!iter->edid)
+ if (!iter->drm_edid)
return NULL;
if (iter->section) {
@@ -96,7 +96,7 @@ __displayid_iter_next(struct displayid_iter *iter)
block = displayid_iter_block(iter);
if (WARN_ON(!block)) {
iter->section = NULL;
- iter->edid = NULL;
+ iter->drm_edid = NULL;
return NULL;
}
@@ -109,12 +109,12 @@ __displayid_iter_next(struct displayid_iter *iter)
}
for (;;) {
- iter->section = drm_find_displayid_extension(iter->edid,
+ iter->section = drm_find_displayid_extension(iter->drm_edid,
&iter->length,
&iter->idx,
&iter->ext_index);
if (!iter->section) {
- iter->edid = NULL;
+ iter->drm_edid = NULL;
return NULL;
}
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index bc43e1b32092..929fc0e46751 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -45,10 +45,6 @@
#include "drm_crtc_internal.h"
-#define version_greater(edid, maj, min) \
- (((edid)->version > (maj)) || \
- ((edid)->version == (maj) && (edid)->revision > (min)))
-
static int oui(u8 first, u8 second, u8 third)
{
return (first << 16) | (second << 8) | third;
@@ -96,7 +92,7 @@ static int oui(u8 first, u8 second, u8 third)
struct detailed_mode_closure {
struct drm_connector *connector;
- const struct edid *edid;
+ const struct drm_edid *drm_edid;
bool preferred;
u32 quirks;
int modes;
@@ -1567,6 +1563,24 @@ static const struct drm_display_mode edid_4k_modes[] = {
/*** DDC fetch and block validation ***/
+/*
+ * The opaque EDID type, internal to drm_edid.c.
+ */
+struct drm_edid {
+ /* Size allocated for edid */
+ size_t size;
+ const struct edid *edid;
+};
+
+static bool version_greater(const struct drm_edid *drm_edid,
+ u8 version, u8 revision)
+{
+ const struct edid *edid = drm_edid->edid;
+
+ return edid->version > version ||
+ (edid->version == version && edid->revision > revision);
+}
+
static int edid_extension_block_count(const struct edid *edid)
{
return edid->extensions;
@@ -1599,6 +1613,72 @@ static const void *edid_extension_block_data(const struct edid *edid, int index)
return edid_block_data(edid, index + 1);
}
+/*
+ * Initializer helper for legacy interfaces, where we have no choice but to
+ * trust edid size. Not for general purpose use.
+ */
+static const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid,
+ const struct edid *edid)
+{
+ if (!edid)
+ return NULL;
+
+ memset(drm_edid, 0, sizeof(*drm_edid));
+
+ drm_edid->edid = edid;
+ drm_edid->size = edid_size(edid);
+
+ return drm_edid;
+}
+
+/*
+ * EDID base and extension block iterator.
+ *
+ * struct drm_edid_iter iter;
+ * const u8 *block;
+ *
+ * drm_edid_iter_begin(drm_edid, &iter);
+ * drm_edid_iter_for_each(block, &iter) {
+ * // do stuff with block
+ * }
+ * drm_edid_iter_end(&iter);
+ */
+struct drm_edid_iter {
+ const struct drm_edid *drm_edid;
+
+ /* Current block index. */
+ int index;
+};
+
+static void drm_edid_iter_begin(const struct drm_edid *drm_edid,
+ struct drm_edid_iter *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+
+ iter->drm_edid = drm_edid;
+}
+
+static const void *__drm_edid_iter_next(struct drm_edid_iter *iter)
+{
+ const void *block = NULL;
+
+ if (!iter->drm_edid)
+ return NULL;
+
+ if (iter->index < edid_block_count(iter->drm_edid->edid))
+ block = edid_block_data(iter->drm_edid->edid, iter->index++);
+
+ return block;
+}
+
+#define drm_edid_iter_for_each(__block, __iter) \
+ while (((__block) = __drm_edid_iter_next(__iter)))
+
+static void drm_edid_iter_end(struct drm_edid_iter *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+}
+
static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
@@ -2362,13 +2442,13 @@ EXPORT_SYMBOL(drm_edid_duplicate);
/**
* edid_get_quirks - return quirk flags for a given EDID
- * @edid: EDID to process
+ * @drm_edid: EDID to process
*
* This tells subsequent routines what fixes they need to apply.
*/
-static u32 edid_get_quirks(const struct edid *edid)
+static u32 edid_get_quirks(const struct drm_edid *drm_edid)
{
- u32 panel_id = edid_extract_panel_id(edid);
+ u32 panel_id = edid_extract_panel_id(drm_edid->edid);
const struct edid_quirk *quirk;
int i;
@@ -2523,20 +2603,21 @@ vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
cb((const struct detailed_timing *)(det_base + 18 * i), closure);
}
-static void
-drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure)
+static void drm_for_each_detailed_block(const struct drm_edid *drm_edid,
+ detailed_cb *cb, void *closure)
{
+ struct drm_edid_iter edid_iter;
+ const u8 *ext;
int i;
- if (edid == NULL)
+ if (!drm_edid)
return;
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
- cb(&(edid->detailed_timings[i]), closure);
-
- for (i = 0; i < edid_extension_block_count(edid); i++) {
- const u8 *ext = edid_extension_block_data(edid, i);
+ cb(&drm_edid->edid->detailed_timings[i], closure);
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(ext, &edid_iter) {
switch (*ext) {
case CEA_EXT:
cea_for_each_detailed_block(ext, cb, closure);
@@ -2548,6 +2629,7 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos
break;
}
}
+ drm_edid_iter_end(&edid_iter);
}
static void
@@ -2568,16 +2650,16 @@ is_rb(const struct detailed_timing *descriptor, void *data)
/* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */
static bool
-drm_monitor_supports_rb(const struct edid *edid)
+drm_monitor_supports_rb(const struct drm_edid *drm_edid)
{
- if (edid->revision >= 4) {
+ if (drm_edid->edid->revision >= 4) {
bool ret = false;
- drm_for_each_detailed_block(edid, is_rb, &ret);
+ drm_for_each_detailed_block(drm_edid, is_rb, &ret);
return ret;
}
- return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
+ return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
}
static void
@@ -2596,11 +2678,11 @@ find_gtf2(const struct detailed_timing *descriptor, void *data)
/* Secondary GTF curve kicks in above some break frequency */
static int
-drm_gtf2_hbreak(const struct edid *edid)
+drm_gtf2_hbreak(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12);
@@ -2608,11 +2690,11 @@ drm_gtf2_hbreak(const struct edid *edid)
}
static int
-drm_gtf2_2c(const struct edid *edid)
+drm_gtf2_2c(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13);
@@ -2620,11 +2702,11 @@ drm_gtf2_2c(const struct edid *edid)
}
static int
-drm_gtf2_m(const struct edid *edid)
+drm_gtf2_m(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14);
@@ -2632,11 +2714,11 @@ drm_gtf2_m(const struct edid *edid)
}
static int
-drm_gtf2_k(const struct edid *edid)
+drm_gtf2_k(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16);
@@ -2644,11 +2726,11 @@ drm_gtf2_k(const struct edid *edid)
}
static int
-drm_gtf2_2j(const struct edid *edid)
+drm_gtf2_2j(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17);
@@ -2656,12 +2738,14 @@ drm_gtf2_2j(const struct edid *edid)
}
/* Get standard timing level (CVT/GTF/DMT). */
-static int standard_timing_level(const struct edid *edid)
+static int standard_timing_level(const struct drm_edid *drm_edid)
{
+ const struct edid *edid = drm_edid->edid;
+
if (edid->revision >= 2) {
if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
return LEVEL_CVT;
- if (drm_gtf2_hbreak(edid))
+ if (drm_gtf2_hbreak(drm_edid))
return LEVEL_GTF2;
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
return LEVEL_GTF;
@@ -2693,9 +2777,9 @@ static int drm_mode_hsync(const struct drm_display_mode *mode)
* Take the standard timing params (in this case width, aspect, and refresh)
* and convert them into a real mode using CVT/GTF/DMT.
*/
-static struct drm_display_mode *
-drm_mode_std(struct drm_connector *connector, const struct edid *edid,
- const struct std_timing *t)
+static struct drm_display_mode *drm_mode_std(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct std_timing *t)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *m, *mode = NULL;
@@ -2705,7 +2789,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
>> EDID_TIMING_ASPECT_SHIFT;
unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
>> EDID_TIMING_VFREQ_SHIFT;
- int timing_level = standard_timing_level(edid);
+ int timing_level = standard_timing_level(drm_edid);
if (bad_std_timing(t->hsize, t->vfreq_aspect))
return NULL;
@@ -2716,7 +2800,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
vrefresh_rate = vfreq + 60;
/* the vdisplay is calculated based on the aspect ratio */
if (aspect_ratio == 0) {
- if (edid->revision < 3)
+ if (drm_edid->edid->revision < 3)
vsize = hsize;
else
vsize = (hsize * 10) / 16;
@@ -2759,7 +2843,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
}
/* check whether it can be found in default mode table */
- if (drm_monitor_supports_rb(edid)) {
+ if (drm_monitor_supports_rb(drm_edid)) {
mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
true);
if (mode)
@@ -2785,14 +2869,14 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
if (!mode)
return NULL;
- if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
+ if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) {
drm_mode_destroy(dev, mode);
mode = drm_gtf_mode_complex(dev, hsize, vsize,
vrefresh_rate, 0, 0,
- drm_gtf2_m(edid),
- drm_gtf2_2c(edid),
- drm_gtf2_k(edid),
- drm_gtf2_2j(edid));
+ drm_gtf2_m(drm_edid),
+ drm_gtf2_2c(drm_edid),
+ drm_gtf2_k(drm_edid),
+ drm_gtf2_2j(drm_edid));
}
break;
case LEVEL_CVT:
@@ -2851,7 +2935,7 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
* drm_display_mode.
*/
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
- const struct edid *edid,
+ const struct drm_edid *drm_edid,
const struct detailed_timing *timing,
u32 quirks)
{
@@ -2939,8 +3023,8 @@ set_size:
}
if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
- mode->width_mm = edid->width_cm * 10;
- mode->height_mm = edid->height_cm * 10;
+ mode->width_mm = drm_edid->edid->width_cm * 10;
+ mode->height_mm = drm_edid->edid->height_cm * 10;
}
mode->type = DRM_MODE_TYPE_DRIVER;
@@ -2998,10 +3082,11 @@ range_pixel_clock(const struct edid *edid, const u8 *t)
return t[9] * 10000 + 5001;
}
-static bool
-mode_in_range(const struct drm_display_mode *mode, const struct edid *edid,
- const struct detailed_timing *timing)
+static bool mode_in_range(const struct drm_display_mode *mode,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
+ const struct edid *edid = drm_edid->edid;
u32 max_clock;
const u8 *t = (const u8 *)timing;
@@ -3020,7 +3105,7 @@ mode_in_range(const struct drm_display_mode *mode, const struct edid *edid,
if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
return false;
- if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
+ if (mode_is_rb(mode) && !drm_monitor_supports_rb(drm_edid))
return false;
return true;
@@ -3044,16 +3129,16 @@ static bool valid_inferred_mode(const struct drm_connector *connector,
return ok;
}
-static int
-drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_dmt_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
- if (mode_in_range(drm_dmt_modes + i, edid, timing) &&
+ if (mode_in_range(drm_dmt_modes + i, drm_edid, timing) &&
valid_inferred_mode(connector, drm_dmt_modes + i)) {
newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
if (newmode) {
@@ -3079,9 +3164,9 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode)
}
}
-static int
-drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_gtf_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
@@ -3095,7 +3180,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
drm_mode_fixup_1366x768(newmode);
- if (!mode_in_range(newmode, edid, timing) ||
+ if (!mode_in_range(newmode, drm_edid, timing) ||
!valid_inferred_mode(connector, newmode)) {
drm_mode_destroy(dev, newmode);
continue;
@@ -3108,14 +3193,14 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
}
-static int
-drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_cvt_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
- bool rb = drm_monitor_supports_rb(edid);
+ bool rb = drm_monitor_supports_rb(drm_edid);
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
@@ -3125,7 +3210,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
drm_mode_fixup_1366x768(newmode);
- if (!mode_in_range(newmode, edid, timing) ||
+ if (!mode_in_range(newmode, drm_edid, timing) ||
!valid_inferred_mode(connector, newmode)) {
drm_mode_destroy(dev, newmode);
continue;
@@ -3149,25 +3234,25 @@ do_inferred_modes(const struct detailed_timing *timing, void *c)
return;
closure->modes += drm_dmt_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
- if (!version_greater(closure->edid, 1, 1))
+ if (!version_greater(closure->drm_edid, 1, 1))
return; /* GTF not defined yet */
switch (range->flags) {
case 0x02: /* secondary gtf, XXX could do more */
case 0x00: /* default gtf */
closure->modes += drm_gtf_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
break;
case 0x04: /* cvt, only in 1.4+ */
- if (!version_greater(closure->edid, 1, 3))
+ if (!version_greater(closure->drm_edid, 1, 3))
break;
closure->modes += drm_cvt_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
break;
case 0x01: /* just the ranges, no formula */
@@ -3176,16 +3261,16 @@ do_inferred_modes(const struct detailed_timing *timing, void *c)
}
}
-static int
-add_inferred_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_inferred_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_inferred_modes, &closure);
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure);
return closure.modes;
}
@@ -3235,17 +3320,18 @@ do_established_modes(const struct detailed_timing *timing, void *c)
* bitmap of the supported "established modes" list (defined above). Tease them
* out and add them to the global modes list.
*/
-static int
-add_established_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_established_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_device *dev = connector->dev;
+ const struct edid *edid = drm_edid->edid;
unsigned long est_bits = edid->established_timings.t1 |
(edid->established_timings.t2 << 8) |
((edid->established_timings.mfg_rsvd & 0x80) << 9);
int i, modes = 0;
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
for (i = 0; i <= EDID_EST_TIMINGS; i++) {
@@ -3260,8 +3346,8 @@ add_established_modes(struct drm_connector *connector, const struct edid *edid)
}
}
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_established_modes,
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_established_modes,
&closure);
return modes + closure.modes;
@@ -3273,7 +3359,6 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
struct detailed_mode_closure *closure = c;
const struct detailed_non_pixel *data = &timing->data.other_data;
struct drm_connector *connector = closure->connector;
- const struct edid *edid = closure->edid;
int i;
if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES))
@@ -3283,7 +3368,7 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
const struct std_timing *std = &data->data.timings[i];
struct drm_display_mode *newmode;
- newmode = drm_mode_std(connector, edid, std);
+ newmode = drm_mode_std(connector, closure->drm_edid, std);
if (newmode) {
drm_mode_probed_add(connector, newmode);
closure->modes++;
@@ -3296,28 +3381,28 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
* using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and
* add them to the list.
*/
-static int
-add_standard_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_standard_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
int i, modes = 0;
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
for (i = 0; i < EDID_STD_TIMINGS; i++) {
struct drm_display_mode *newmode;
- newmode = drm_mode_std(connector, edid,
- &edid->standard_timings[i]);
+ newmode = drm_mode_std(connector, drm_edid,
+ &drm_edid->edid->standard_timings[i]);
if (newmode) {
drm_mode_probed_add(connector, newmode);
modes++;
}
}
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_standard_modes,
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_standard_modes,
&closure);
/* XXX should also look for standard codes in VTB blocks */
@@ -3389,15 +3474,15 @@ do_cvt_mode(const struct detailed_timing *timing, void *c)
}
static int
-add_cvt_modes(struct drm_connector *connector, const struct edid *edid)
+add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
- if (version_greater(edid, 1, 2))
- drm_for_each_detailed_block(edid, do_cvt_mode, &closure);
+ if (version_greater(drm_edid, 1, 2))
+ drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure);
/* XXX should also look for CVT codes in VTB blocks */
@@ -3416,7 +3501,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
return;
newmode = drm_mode_detailed(closure->connector->dev,
- closure->edid, timing,
+ closure->drm_edid, timing,
closure->quirks);
if (!newmode)
return;
@@ -3439,38 +3524,43 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
/*
* add_detailed_modes - Add modes from detailed timings
* @connector: attached connector
- * @edid: EDID block to scan
+ * @drm_edid: EDID block to scan
* @quirks: quirks to apply
*/
-static int
-add_detailed_modes(struct drm_connector *connector, const struct edid *edid,
- u32 quirks)
+static int add_detailed_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid, u32 quirks)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
.preferred = true,
.quirks = quirks,
};
- if (closure.preferred && !version_greater(edid, 1, 3))
+ if (closure.preferred && !version_greater(drm_edid, 1, 3))
closure.preferred =
- (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+ (drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
- drm_for_each_detailed_block(edid, do_detailed_mode, &closure);
+ drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure);
return closure.modes;
}
-#define AUDIO_BLOCK 0x01
-#define VIDEO_BLOCK 0x02
-#define VENDOR_BLOCK 0x03
-#define SPEAKER_BLOCK 0x04
-#define HDR_STATIC_METADATA_BLOCK 0x6
-#define USE_EXTENDED_TAG 0x07
-#define EXT_VIDEO_CAPABILITY_BLOCK 0x00
-#define EXT_VIDEO_DATA_BLOCK_420 0x0E
-#define EXT_VIDEO_CAP_BLOCK_Y420CMDB 0x0F
+/* CTA-861-H Table 60 - CTA Tag Codes */
+#define CTA_DB_AUDIO 1
+#define CTA_DB_VIDEO 2
+#define CTA_DB_VENDOR 3
+#define CTA_DB_SPEAKER 4
+#define CTA_DB_EXTENDED_TAG 7
+
+/* CTA-861-H Table 62 - CTA Extended Tag Codes */
+#define CTA_EXT_DB_VIDEO_CAP 0
+#define CTA_EXT_DB_VENDOR 1
+#define CTA_EXT_DB_HDR_STATIC_METADATA 6
+#define CTA_EXT_DB_420_VIDEO_DATA 14
+#define CTA_EXT_DB_420_VIDEO_CAP_MAP 15
+#define CTA_EXT_DB_HF_SCDB 0x79
+
#define EDID_BASIC_AUDIO (1 << 6)
#define EDID_CEA_YCRCB444 (1 << 5)
#define EDID_CEA_YCRCB422 (1 << 4)
@@ -3478,10 +3568,13 @@ add_detailed_modes(struct drm_connector *connector, const struct edid *edid,
/*
* Search EDID for CEA extension block.
+ *
+ * FIXME: Prefer not returning pointers to raw EDID data.
*/
-const u8 *drm_find_edid_extension(const struct edid *edid,
+const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
int ext_id, int *ext_index)
{
+ const struct edid *edid = drm_edid ? drm_edid->edid : NULL;
const u8 *edid_ext = NULL;
int i;
@@ -3504,30 +3597,29 @@ const u8 *drm_find_edid_extension(const struct edid *edid,
return edid_ext;
}
-static const u8 *drm_find_cea_extension(const struct edid *edid)
+/* Return true if the EDID has a CTA extension or a DisplayID CTA data block */
+static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
- const u8 *cea;
int ext_index = 0;
+ bool found = false;
/* Look for a top level CEA extension block */
- /* FIXME: make callers iterate through multiple CEA ext blocks? */
- cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index);
- if (cea)
- return cea;
+ if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index))
+ return true;
/* CEA blocks can also be found embedded in a DisplayID block */
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_CTA) {
- cea = (const u8 *)block;
+ found = true;
break;
}
}
displayid_iter_end(&iter);
- return cea;
+ return found;
}
static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic)
@@ -3792,16 +3884,16 @@ static bool drm_valid_hdmi_vic(u8 vic)
return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
}
-static int
-add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_alternate_cea_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *tmp;
LIST_HEAD(list);
int modes = 0;
- /* Don't add CEA modes if the CEA extension block is missing */
- if (!drm_find_cea_extension(edid))
+ /* Don't add CTA modes if the CTA extension block is missing */
+ if (!drm_edid_has_cta_extension(drm_edid))
return 0;
/*
@@ -4283,24 +4375,6 @@ out:
}
static int
-cea_db_payload_len(const u8 *db)
-{
- return db[0] & 0x1f;
-}
-
-static int
-cea_db_extended_tag(const u8 *db)
-{
- return db[1];
-}
-
-static int
-cea_db_tag(const u8 *db)
-{
- return db[0] >> 5;
-}
-
-static int
cea_revision(const u8 *cea)
{
/*
@@ -4313,125 +4387,255 @@ cea_revision(const u8 *cea)
return cea[1];
}
-static int
-cea_db_offsets(const u8 *cea, int *start, int *end)
+/*
+ * CTA Data Block iterator.
+ *
+ * Iterate through all CTA Data Blocks in both EDID CTA Extensions and DisplayID
+ * CTA Data Blocks.
+ *
+ * struct cea_db *db:
+ * struct cea_db_iter iter;
+ *
+ * cea_db_iter_edid_begin(edid, &iter);
+ * cea_db_iter_for_each(db, &iter) {
+ * // do stuff with db
+ * }
+ * cea_db_iter_end(&iter);
+ */
+struct cea_db_iter {
+ struct drm_edid_iter edid_iter;
+ struct displayid_iter displayid_iter;
+
+ /* Current Data Block Collection. */
+ const u8 *collection;
+
+ /* Current Data Block index in current collection. */
+ int index;
+
+ /* End index in current collection. */
+ int end;
+};
+
+/* CTA-861-H section 7.4 CTA Data BLock Collection */
+struct cea_db {
+ u8 tag_length;
+ u8 data[];
+} __packed;
+
+static int cea_db_tag(const struct cea_db *db)
{
- /* DisplayID CTA extension blocks and top-level CEA EDID
- * block header definitions differ in the following bytes:
- * 1) Byte 2 of the header specifies length differently,
- * 2) Byte 3 is only present in the CEA top level block.
- *
- * The different definitions for byte 2 follow.
- *
- * DisplayID CTA extension block defines byte 2 as:
- * Number of payload bytes
- *
- * CEA EDID block defines byte 2 as:
- * Byte number (decimal) within this block where the 18-byte
- * DTDs begin. If no non-DTD data is present in this extension
- * block, the value should be set to 04h (the byte after next).
- * If set to 00h, there are no DTDs present in this block and
- * no non-DTD data.
- */
- if (cea[0] == DATA_BLOCK_CTA) {
- /*
- * for_each_displayid_db() has already verified
- * that these stay within expected bounds.
- */
- *start = 3;
- *end = *start + cea[2];
- } else if (cea[0] == CEA_EXT) {
- /* Data block offset in CEA extension block */
- *start = 4;
- *end = cea[2];
- if (*end == 0)
- *end = 127;
- if (*end < 4 || *end > 127)
- return -ERANGE;
- } else {
- return -EOPNOTSUPP;
- }
+ return db->tag_length >> 5;
+}
- return 0;
+static int cea_db_payload_len(const void *_db)
+{
+ /* FIXME: Transition to passing struct cea_db * everywhere. */
+ const struct cea_db *db = _db;
+
+ return db->tag_length & 0x1f;
}
-static bool cea_db_is_hdmi_vsdb(const u8 *db)
+static const void *cea_db_data(const struct cea_db *db)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ return db->data;
+}
- if (cea_db_payload_len(db) < 5)
- return false;
+static bool cea_db_is_extended_tag(const struct cea_db *db, int tag)
+{
+ return cea_db_tag(db) == CTA_DB_EXTENDED_TAG &&
+ cea_db_payload_len(db) >= 1 &&
+ db->data[0] == tag;
+}
- return oui(db[3], db[2], db[1]) == HDMI_IEEE_OUI;
+static bool cea_db_is_vendor(const struct cea_db *db, int vendor_oui)
+{
+ const u8 *data = cea_db_data(db);
+
+ return cea_db_tag(db) == CTA_DB_VENDOR &&
+ cea_db_payload_len(db) >= 3 &&
+ oui(data[2], data[1], data[0]) == vendor_oui;
}
-static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
+static void cea_db_iter_edid_begin(const struct drm_edid *drm_edid,
+ struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ memset(iter, 0, sizeof(*iter));
- if (cea_db_payload_len(db) < 7)
- return false;
+ drm_edid_iter_begin(drm_edid, &iter->edid_iter);
+ displayid_iter_edid_begin(drm_edid, &iter->displayid_iter);
+}
+
+static const struct cea_db *
+__cea_db_iter_current_block(const struct cea_db_iter *iter)
+{
+ const struct cea_db *db;
+
+ if (!iter->collection)
+ return NULL;
- return oui(db[3], db[2], db[1]) == HDMI_FORUM_IEEE_OUI;
+ db = (const struct cea_db *)&iter->collection[iter->index];
+
+ if (iter->index + sizeof(*db) <= iter->end &&
+ iter->index + sizeof(*db) + cea_db_payload_len(db) <= iter->end)
+ return db;
+
+ return NULL;
}
-static bool cea_db_is_microsoft_vsdb(const u8 *db)
+/*
+ * References:
+ * - VESA E-EDID v1.4
+ * - CTA-861-H section 7.3.3 CTA Extension Version 3
+ */
+static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ const u8 *ext;
- if (cea_db_payload_len(db) != 21)
- return false;
+ drm_edid_iter_for_each(ext, &iter->edid_iter) {
+ /* Only support CTA Extension revision 3+ */
+ if (ext[0] != CEA_EXT || cea_revision(ext) < 3)
+ continue;
+
+ iter->index = 4;
+ iter->end = ext[2];
+ if (iter->end == 0)
+ iter->end = 127;
+ if (iter->end < 4 || iter->end > 127)
+ continue;
+
+ return ext;
+ }
- return oui(db[3], db[2], db[1]) == MICROSOFT_IEEE_OUI;
+ return NULL;
}
-static bool cea_db_is_vcdb(const u8 *db)
+/*
+ * References:
+ * - DisplayID v1.3 Appendix C: CEA Data Block within a DisplayID Data Block
+ * - DisplayID v2.0 section 4.10 CTA DisplayID Data Block
+ *
+ * Note that the above do not specify any connection between DisplayID Data
+ * Block revision and CTA Extension versions.
+ */
+static const void *__cea_db_iter_displayid_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ const struct displayid_block *block;
- if (cea_db_payload_len(db) != 2)
- return false;
+ displayid_iter_for_each(block, &iter->displayid_iter) {
+ if (block->tag != DATA_BLOCK_CTA)
+ continue;
- if (cea_db_extended_tag(db) != EXT_VIDEO_CAPABILITY_BLOCK)
- return false;
+ /*
+ * The displayid iterator has already verified the block bounds
+ * in displayid_iter_block().
+ */
+ iter->index = sizeof(*block);
+ iter->end = iter->index + block->num_bytes;
- return true;
+ return block;
+ }
+
+ return NULL;
}
-static bool cea_db_is_y420cmdb(const u8 *db)
+static const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ const struct cea_db *db;
- if (!cea_db_payload_len(db))
- return false;
+ if (iter->collection) {
+ /* Current collection should always be valid. */
+ db = __cea_db_iter_current_block(iter);
+ if (WARN_ON(!db)) {
+ iter->collection = NULL;
+ return NULL;
+ }
- if (cea_db_extended_tag(db) != EXT_VIDEO_CAP_BLOCK_Y420CMDB)
- return false;
+ /* Next block in CTA Data Block Collection */
+ iter->index += sizeof(*db) + cea_db_payload_len(db);
- return true;
+ db = __cea_db_iter_current_block(iter);
+ if (db)
+ return db;
+ }
+
+ for (;;) {
+ /*
+ * Find the next CTA Data Block Collection. First iterate all
+ * the EDID CTA Extensions, then all the DisplayID CTA blocks.
+ *
+ * Per DisplayID v1.3 Appendix B: DisplayID as an EDID
+ * Extension, it's recommended that DisplayID extensions are
+ * exposed after all of the CTA Extensions.
+ */
+ iter->collection = __cea_db_iter_edid_next(iter);
+ if (!iter->collection)
+ iter->collection = __cea_db_iter_displayid_next(iter);
+
+ if (!iter->collection)
+ return NULL;
+
+ db = __cea_db_iter_current_block(iter);
+ if (db)
+ return db;
+ }
}
-static bool cea_db_is_y420vdb(const u8 *db)
+#define cea_db_iter_for_each(__db, __iter) \
+ while (((__db) = __cea_db_iter_next(__iter)))
+
+static void cea_db_iter_end(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ displayid_iter_end(&iter->displayid_iter);
+ drm_edid_iter_end(&iter->edid_iter);
- if (!cea_db_payload_len(db))
- return false;
+ memset(iter, 0, sizeof(*iter));
+}
- if (cea_db_extended_tag(db) != EXT_VIDEO_DATA_BLOCK_420)
- return false;
+static bool cea_db_is_hdmi_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, HDMI_IEEE_OUI) &&
+ cea_db_payload_len(db) >= 5;
+}
- return true;
+static bool cea_db_is_hdmi_forum_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, HDMI_FORUM_IEEE_OUI) &&
+ cea_db_payload_len(db) >= 7;
+}
+
+static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) &&
+ cea_db_payload_len(db) == 21;
+}
+
+static bool cea_db_is_vcdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
+ cea_db_payload_len(db) == 2;
+}
+
+static bool cea_db_is_hdmi_forum_scdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_SCDB) &&
+ cea_db_payload_len(db) >= 7;
}
-#define for_each_cea_db(cea, i, start, end) \
- for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
+static bool cea_db_is_y420cmdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_CAP_MAP);
+}
+
+static bool cea_db_is_y420vdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_DATA);
+}
+
+static bool cea_db_is_hdmi_hdr_metadata_block(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) &&
+ cea_db_payload_len(db) >= 3;
+}
static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
const u8 *db)
@@ -4473,49 +4677,44 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
hdmi->y420_cmdb_map = map;
}
-static int
-add_cea_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_cea_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
- const u8 *cea = drm_find_cea_extension(edid);
- const u8 *db, *hdmi = NULL, *video = NULL;
- u8 dbl, hdmi_len, video_len = 0;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
int modes = 0;
- if (cea && cea_revision(cea) >= 3) {
- int i, start, end;
-
- if (cea_db_offsets(cea, &start, &end))
- return 0;
-
- for_each_cea_db(cea, i, start, end) {
- db = &cea[i];
- dbl = cea_db_payload_len(db);
-
- if (cea_db_tag(db) == VIDEO_BLOCK) {
- video = db + 1;
- video_len = dbl;
- modes += do_cea_modes(connector, video, dbl);
- } else if (cea_db_is_hdmi_vsdb(db)) {
- hdmi = db;
- hdmi_len = dbl;
- } else if (cea_db_is_y420vdb(db)) {
- const u8 *vdb420 = &db[2];
-
- /* Add 4:2:0(only) modes present in EDID */
- modes += do_y420vdb_modes(connector,
- vdb420,
- dbl - 1);
- }
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ const u8 *hdmi = NULL, *video = NULL;
+ u8 hdmi_len = 0, video_len = 0;
+
+ if (cea_db_tag(db) == CTA_DB_VIDEO) {
+ video = cea_db_data(db);
+ video_len = cea_db_payload_len(db);
+ modes += do_cea_modes(connector, video, video_len);
+ } else if (cea_db_is_hdmi_vsdb(db)) {
+ /* FIXME: Switch to use cea_db_data() */
+ hdmi = (const u8 *)db;
+ hdmi_len = cea_db_payload_len(db);
+ } else if (cea_db_is_y420vdb(db)) {
+ const u8 *vdb420 = cea_db_data(db) + 1;
+
+ /* Add 4:2:0(only) modes present in EDID */
+ modes += do_y420vdb_modes(connector, vdb420,
+ cea_db_payload_len(db) - 1);
}
- }
- /*
- * We parse the HDMI VSDB after having added the cea modes as we will
- * be patching their flags when the sink supports stereo 3D.
- */
- if (hdmi)
- modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
- video_len);
+ /*
+ * We parse the HDMI VSDB after having added the cea modes as we
+ * will be patching their flags when the sink supports stereo
+ * 3D.
+ */
+ if (hdmi)
+ modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len,
+ video, video_len);
+ }
+ cea_db_iter_end(&iter);
return modes;
}
@@ -4563,20 +4762,6 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
mode->clock = clock;
}
-static bool cea_db_is_hdmi_hdr_metadata_block(const u8 *db)
-{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
-
- if (db[1] != HDR_STATIC_METADATA_BLOCK)
- return false;
-
- if (cea_db_payload_len(db) < 3)
- return false;
-
- return true;
-}
-
static uint8_t eotf_supported(const u8 *edid_ext)
{
return edid_ext[2] &
@@ -4654,15 +4839,15 @@ monitor_name(const struct detailed_timing *timing, void *data)
*res = timing->data.other_data.data.str.str;
}
-static int get_monitor_name(const struct edid *edid, char name[13])
+static int get_monitor_name(const struct drm_edid *drm_edid, char name[13])
{
const char *edid_name = NULL;
int mnl;
- if (!edid || !name)
+ if (!drm_edid || !name)
return 0;
- drm_for_each_detailed_block(edid, monitor_name, &edid_name);
+ drm_for_each_detailed_block(drm_edid, monitor_name, &edid_name);
for (mnl = 0; edid_name && mnl < 13; mnl++) {
if (edid_name[mnl] == 0x0a)
break;
@@ -4682,14 +4867,22 @@ static int get_monitor_name(const struct edid *edid, char name[13])
*/
void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize)
{
- int name_length;
- char buf[13];
+ int name_length = 0;
if (bufsize <= 0)
return;
- name_length = min(get_monitor_name(edid, buf), bufsize - 1);
- memcpy(name, buf, name_length);
+ if (edid) {
+ char buf[13];
+ struct drm_edid drm_edid = {
+ .edid = edid,
+ .size = edid_size(edid),
+ };
+
+ name_length = min(get_monitor_name(&drm_edid, buf), bufsize - 1);
+ memcpy(name, buf, name_length);
+ }
+
name[name_length] = '\0';
}
EXPORT_SYMBOL(drm_edid_get_monitor_name);
@@ -4709,82 +4902,70 @@ static void clear_eld(struct drm_connector *connector)
/*
* drm_edid_to_eld - build ELD from EDID
* @connector: connector corresponding to the HDMI/DP sink
- * @edid: EDID to parse
+ * @drm_edid: EDID to parse
*
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
* HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
*/
static void drm_edid_to_eld(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
+ const struct drm_display_info *info = &connector->display_info;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
uint8_t *eld = connector->eld;
- const u8 *cea;
- const u8 *db;
int total_sad_count = 0;
int mnl;
- int dbl;
clear_eld(connector);
- if (!edid)
+ if (!drm_edid)
return;
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
- return;
- }
-
- mnl = get_monitor_name(edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
+ mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]);
- eld[DRM_ELD_CEA_EDID_VER_MNL] = cea[1] << DRM_ELD_CEA_EDID_VER_SHIFT;
+ eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT;
eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl;
eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D;
- eld[DRM_ELD_MANUFACTURER_NAME0] = edid->mfg_id[0];
- eld[DRM_ELD_MANUFACTURER_NAME1] = edid->mfg_id[1];
- eld[DRM_ELD_PRODUCT_CODE0] = edid->prod_code[0];
- eld[DRM_ELD_PRODUCT_CODE1] = edid->prod_code[1];
+ eld[DRM_ELD_MANUFACTURER_NAME0] = drm_edid->edid->mfg_id[0];
+ eld[DRM_ELD_MANUFACTURER_NAME1] = drm_edid->edid->mfg_id[1];
+ eld[DRM_ELD_PRODUCT_CODE0] = drm_edid->edid->prod_code[0];
+ eld[DRM_ELD_PRODUCT_CODE1] = drm_edid->edid->prod_code[1];
- if (cea_revision(cea) >= 3) {
- int i, start, end;
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ const u8 *data = cea_db_data(db);
+ int len = cea_db_payload_len(db);
int sad_count;
- if (cea_db_offsets(cea, &start, &end)) {
- start = 0;
- end = 0;
- }
-
- for_each_cea_db(cea, i, start, end) {
- db = &cea[i];
- dbl = cea_db_payload_len(db);
-
- switch (cea_db_tag(db)) {
- case AUDIO_BLOCK:
- /* Audio Data Block, contains SADs */
- sad_count = min(dbl / 3, 15 - total_sad_count);
- if (sad_count >= 1)
- memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
- &db[1], sad_count * 3);
- total_sad_count += sad_count;
- break;
- case SPEAKER_BLOCK:
- /* Speaker Allocation Data Block */
- if (dbl >= 1)
- eld[DRM_ELD_SPEAKER] = db[1];
- break;
- case VENDOR_BLOCK:
- /* HDMI Vendor-Specific Data Block */
- if (cea_db_is_hdmi_vsdb(db))
- drm_parse_hdmi_vsdb_audio(connector, db);
- break;
- default:
- break;
- }
+ switch (cea_db_tag(db)) {
+ case CTA_DB_AUDIO:
+ /* Audio Data Block, contains SADs */
+ sad_count = min(len / 3, 15 - total_sad_count);
+ if (sad_count >= 1)
+ memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
+ data, sad_count * 3);
+ total_sad_count += sad_count;
+ break;
+ case CTA_DB_SPEAKER:
+ /* Speaker Allocation Data Block */
+ if (len >= 1)
+ eld[DRM_ELD_SPEAKER] = data[0];
+ break;
+ case CTA_DB_VENDOR:
+ /* HDMI Vendor-Specific Data Block */
+ if (cea_db_is_hdmi_vsdb(db))
+ drm_parse_hdmi_vsdb_audio(connector, (const u8 *)db);
+ break;
+ default:
+ break;
}
}
+ cea_db_iter_end(&iter);
+
eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT;
if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
@@ -4800,6 +4981,40 @@ static void drm_edid_to_eld(struct drm_connector *connector,
drm_eld_size(eld), total_sad_count);
}
+static int _drm_edid_to_sad(const struct drm_edid *drm_edid,
+ struct cea_sad **sads)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ int count = 0;
+
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_AUDIO) {
+ int j;
+
+ count = cea_db_payload_len(db) / 3; /* SAD is 3B */
+ *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
+ if (!*sads)
+ return -ENOMEM;
+ for (j = 0; j < count; j++) {
+ const u8 *sad = &db->data[j * 3];
+
+ (*sads)[j].format = (sad[0] & 0x78) >> 3;
+ (*sads)[j].channels = sad[0] & 0x7;
+ (*sads)[j].freq = sad[1] & 0x7F;
+ (*sads)[j].byte2 = sad[2];
+ }
+ break;
+ }
+ }
+ cea_db_iter_end(&iter);
+
+ DRM_DEBUG_KMS("Found %d Short Audio Descriptors\n", count);
+
+ return count;
+}
+
/**
* drm_edid_to_sad - extracts SADs from EDID
* @edid: EDID to parse
@@ -4813,53 +5028,37 @@ static void drm_edid_to_eld(struct drm_connector *connector,
*/
int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads)
{
- int count = 0;
- int i, start, end, dbl;
- const u8 *cea;
-
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
- return 0;
- }
-
- if (cea_revision(cea) < 3) {
- DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
- return 0;
- }
-
- if (cea_db_offsets(cea, &start, &end)) {
- DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
- return -EPROTO;
- }
-
- for_each_cea_db(cea, i, start, end) {
- const u8 *db = &cea[i];
+ struct drm_edid drm_edid;
- if (cea_db_tag(db) == AUDIO_BLOCK) {
- int j;
+ return _drm_edid_to_sad(drm_edid_legacy_init(&drm_edid, edid), sads);
+}
+EXPORT_SYMBOL(drm_edid_to_sad);
- dbl = cea_db_payload_len(db);
+static int _drm_edid_to_speaker_allocation(const struct drm_edid *drm_edid,
+ u8 **sadb)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ int count = 0;
- count = dbl / 3; /* SAD is 3B */
- *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
- if (!*sads)
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_SPEAKER &&
+ cea_db_payload_len(db) == 3) {
+ *sadb = kmemdup(db->data, cea_db_payload_len(db),
+ GFP_KERNEL);
+ if (!*sadb)
return -ENOMEM;
- for (j = 0; j < count; j++) {
- const u8 *sad = &db[1 + j * 3];
-
- (*sads)[j].format = (sad[0] & 0x78) >> 3;
- (*sads)[j].channels = sad[0] & 0x7;
- (*sads)[j].freq = sad[1] & 0x7F;
- (*sads)[j].byte2 = sad[2];
- }
+ count = cea_db_payload_len(db);
break;
}
}
+ cea_db_iter_end(&iter);
+
+ DRM_DEBUG_KMS("Found %d Speaker Allocation Data Blocks\n", count);
return count;
}
-EXPORT_SYMBOL(drm_edid_to_sad);
/**
* drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
@@ -4875,44 +5074,10 @@ EXPORT_SYMBOL(drm_edid_to_sad);
*/
int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
{
- int count = 0;
- int i, start, end, dbl;
- const u8 *cea;
-
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
- return 0;
- }
-
- if (cea_revision(cea) < 3) {
- DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
- return 0;
- }
+ struct drm_edid drm_edid;
- if (cea_db_offsets(cea, &start, &end)) {
- DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
- return -EPROTO;
- }
-
- for_each_cea_db(cea, i, start, end) {
- const u8 *db = &cea[i];
-
- if (cea_db_tag(db) == SPEAKER_BLOCK) {
- dbl = cea_db_payload_len(db);
-
- /* Speaker Allocation Data Block */
- if (dbl == 3) {
- *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
- if (!*sadb)
- return -ENOMEM;
- count = dbl;
- break;
- }
- }
- }
-
- return count;
+ return _drm_edid_to_speaker_allocation(drm_edid_legacy_init(&drm_edid, edid),
+ sadb);
}
EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
@@ -4957,6 +5122,28 @@ int drm_av_sync_delay(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_av_sync_delay);
+static bool _drm_detect_hdmi_monitor(const struct drm_edid *drm_edid)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ bool hdmi = false;
+
+ /*
+ * Because HDMI identifier is in Vendor Specific Block,
+ * search it from all data blocks of CEA extension.
+ */
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_is_hdmi_vsdb(db)) {
+ hdmi = true;
+ break;
+ }
+ }
+ cea_db_iter_end(&iter);
+
+ return hdmi;
+}
+
/**
* drm_detect_hdmi_monitor - detect whether monitor is HDMI
* @edid: monitor EDID information
@@ -4970,29 +5157,53 @@ EXPORT_SYMBOL(drm_av_sync_delay);
*/
bool drm_detect_hdmi_monitor(const struct edid *edid)
{
+ struct drm_edid drm_edid;
+
+ return _drm_detect_hdmi_monitor(drm_edid_legacy_init(&drm_edid, edid));
+}
+EXPORT_SYMBOL(drm_detect_hdmi_monitor);
+
+static bool _drm_detect_monitor_audio(const struct drm_edid *drm_edid)
+{
+ struct drm_edid_iter edid_iter;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
const u8 *edid_ext;
- int i;
- int start_offset, end_offset;
+ bool has_audio = false;
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- return false;
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(edid_ext, &edid_iter) {
+ if (edid_ext[0] == CEA_EXT) {
+ has_audio = edid_ext[3] & EDID_BASIC_AUDIO;
+ if (has_audio)
+ break;
+ }
+ }
+ drm_edid_iter_end(&edid_iter);
- if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
- return false;
+ if (has_audio) {
+ DRM_DEBUG_KMS("Monitor has basic audio support\n");
+ goto end;
+ }
- /*
- * Because HDMI identifier is in Vendor Specific Block,
- * search it from all data blocks of CEA extension.
- */
- for_each_cea_db(edid_ext, i, start_offset, end_offset) {
- if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
- return true;
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_AUDIO) {
+ const u8 *data = cea_db_data(db);
+ int i;
+
+ for (i = 0; i < cea_db_payload_len(db); i += 3)
+ DRM_DEBUG_KMS("CEA audio format %d\n",
+ (data[i] >> 3) & 0xf);
+ has_audio = true;
+ break;
+ }
}
+ cea_db_iter_end(&iter);
- return false;
+end:
+ return has_audio;
}
-EXPORT_SYMBOL(drm_detect_hdmi_monitor);
/**
* drm_detect_monitor_audio - check monitor audio capability
@@ -5008,37 +5219,9 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
*/
bool drm_detect_monitor_audio(const struct edid *edid)
{
- const u8 *edid_ext;
- int i, j;
- bool has_audio = false;
- int start_offset, end_offset;
-
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- goto end;
-
- has_audio = (edid_ext[0] == CEA_EXT &&
- (edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+ struct drm_edid drm_edid;
- if (has_audio) {
- DRM_DEBUG_KMS("Monitor has basic audio support\n");
- goto end;
- }
-
- if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
- goto end;
-
- for_each_cea_db(edid_ext, i, start_offset, end_offset) {
- if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
- has_audio = true;
- for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
- DRM_DEBUG_KMS("CEA audio format %d\n",
- (edid_ext[i + j] >> 3) & 0xf);
- goto end;
- }
- }
-end:
- return has_audio;
+ return _drm_detect_monitor_audio(drm_edid_legacy_init(&drm_edid, edid));
}
EXPORT_SYMBOL(drm_detect_monitor_audio);
@@ -5117,17 +5300,18 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
hdmi->y420_dc_modes = dc_mask;
}
-static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
- const u8 *hf_vsdb)
+/* Sink Capability Data Structure */
+static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
+ const u8 *hf_scds)
{
struct drm_display_info *display = &connector->display_info;
struct drm_hdmi_info *hdmi = &display->hdmi;
display->has_hdmi_infoframe = true;
- if (hf_vsdb[6] & 0x80) {
+ if (hf_scds[6] & 0x80) {
hdmi->scdc.supported = true;
- if (hf_vsdb[6] & 0x40)
+ if (hf_scds[6] & 0x40)
hdmi->scdc.read_request = true;
}
@@ -5140,9 +5324,9 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
* Lets check it out.
*/
- if (hf_vsdb[5]) {
+ if (hf_scds[5]) {
/* max clock is 5000 KHz times block value */
- u32 max_tmds_clock = hf_vsdb[5] * 5000;
+ u32 max_tmds_clock = hf_scds[5] * 5000;
struct drm_scdc *scdc = &hdmi->scdc;
if (max_tmds_clock > 340000) {
@@ -5155,42 +5339,42 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
scdc->scrambling.supported = true;
/* Few sinks support scrambling for clocks < 340M */
- if ((hf_vsdb[6] & 0x8))
+ if ((hf_scds[6] & 0x8))
scdc->scrambling.low_rates = true;
}
}
- if (hf_vsdb[7]) {
+ if (hf_scds[7]) {
u8 max_frl_rate;
u8 dsc_max_frl_rate;
u8 dsc_max_slices;
struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n");
- max_frl_rate = (hf_vsdb[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
+ max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes,
&hdmi->max_frl_rate_per_lane);
- hdmi_dsc->v_1p2 = hf_vsdb[11] & DRM_EDID_DSC_1P2;
+ hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2;
if (hdmi_dsc->v_1p2) {
- hdmi_dsc->native_420 = hf_vsdb[11] & DRM_EDID_DSC_NATIVE_420;
- hdmi_dsc->all_bpp = hf_vsdb[11] & DRM_EDID_DSC_ALL_BPP;
+ hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420;
+ hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP;
- if (hf_vsdb[11] & DRM_EDID_DSC_16BPC)
+ if (hf_scds[11] & DRM_EDID_DSC_16BPC)
hdmi_dsc->bpc_supported = 16;
- else if (hf_vsdb[11] & DRM_EDID_DSC_12BPC)
+ else if (hf_scds[11] & DRM_EDID_DSC_12BPC)
hdmi_dsc->bpc_supported = 12;
- else if (hf_vsdb[11] & DRM_EDID_DSC_10BPC)
+ else if (hf_scds[11] & DRM_EDID_DSC_10BPC)
hdmi_dsc->bpc_supported = 10;
else
hdmi_dsc->bpc_supported = 0;
- dsc_max_frl_rate = (hf_vsdb[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
+ dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes,
&hdmi_dsc->max_frl_rate_per_lane);
- hdmi_dsc->total_chunk_kbytes = hf_vsdb[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
+ hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
- dsc_max_slices = hf_vsdb[12] & DRM_EDID_DSC_MAX_SLICES;
+ dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES;
switch (dsc_max_slices) {
case 1:
hdmi_dsc->max_slices = 1;
@@ -5228,7 +5412,7 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
}
}
- drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
+ drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
}
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
@@ -5332,48 +5516,55 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
}
static void drm_parse_cea_ext(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
+ struct drm_edid_iter edid_iter;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
const u8 *edid_ext;
- int i, start, end;
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- return;
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(edid_ext, &edid_iter) {
+ if (edid_ext[0] != CEA_EXT)
+ continue;
- info->cea_rev = edid_ext[1];
+ if (!info->cea_rev)
+ info->cea_rev = edid_ext[1];
- /* The existence of a CEA block should imply RGB support */
- info->color_formats = DRM_COLOR_FORMAT_RGB444;
+ if (info->cea_rev != edid_ext[1])
+ DRM_DEBUG_KMS("CEA extension version mismatch %u != %u\n",
+ info->cea_rev, edid_ext[1]);
- /* CTA DisplayID Data Block does not have byte #3 */
- if (edid_ext[0] == CEA_EXT) {
+ /* The existence of a CTA extension should imply RGB support */
+ info->color_formats = DRM_COLOR_FORMAT_RGB444;
if (edid_ext[3] & EDID_CEA_YCRCB444)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
if (edid_ext[3] & EDID_CEA_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
}
+ drm_edid_iter_end(&edid_iter);
- if (cea_db_offsets(edid_ext, &start, &end))
- return;
-
- for_each_cea_db(edid_ext, i, start, end) {
- const u8 *db = &edid_ext[i];
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ /* FIXME: convert parsers to use struct cea_db */
+ const u8 *data = (const u8 *)db;
if (cea_db_is_hdmi_vsdb(db))
- drm_parse_hdmi_vsdb_video(connector, db);
- if (cea_db_is_hdmi_forum_vsdb(db))
- drm_parse_hdmi_forum_vsdb(connector, db);
- if (cea_db_is_microsoft_vsdb(db))
- drm_parse_microsoft_vsdb(connector, db);
- if (cea_db_is_y420cmdb(db))
- drm_parse_y420cmdb_bitmap(connector, db);
- if (cea_db_is_vcdb(db))
- drm_parse_vcdb(connector, db);
- if (cea_db_is_hdmi_hdr_metadata_block(db))
- drm_parse_hdr_metadata_block(connector, db);
+ drm_parse_hdmi_vsdb_video(connector, data);
+ else if (cea_db_is_hdmi_forum_vsdb(db) ||
+ cea_db_is_hdmi_forum_scdb(db))
+ drm_parse_hdmi_forum_scds(connector, data);
+ else if (cea_db_is_microsoft_vsdb(db))
+ drm_parse_microsoft_vsdb(connector, data);
+ else if (cea_db_is_y420cmdb(db))
+ drm_parse_y420cmdb_bitmap(connector, data);
+ else if (cea_db_is_vcdb(db))
+ drm_parse_vcdb(connector, data);
+ else if (cea_db_is_hdmi_hdr_metadata_block(db))
+ drm_parse_hdr_metadata_block(connector, data);
}
+ cea_db_iter_end(&iter);
}
static
@@ -5400,16 +5591,15 @@ void get_monitor_range(const struct detailed_timing *timing,
monitor_range->max_vfreq = range->max_vfreq;
}
-static
-void drm_get_monitor_range(struct drm_connector *connector,
- const struct edid *edid)
+static void drm_get_monitor_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
- if (!version_greater(edid, 1, 1))
+ if (!version_greater(drm_edid, 1, 1))
return;
- drm_for_each_detailed_block(edid, get_monitor_range,
+ drm_for_each_detailed_block(drm_edid, get_monitor_range,
&info->monitor_range);
DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n",
@@ -5469,12 +5659,13 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector,
info->mso_stream_count, info->mso_pixel_overlap);
}
-static void drm_update_mso(struct drm_connector *connector, const struct edid *edid)
+static void drm_update_mso(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
drm_parse_vesa_mso_data(connector, block);
@@ -5513,18 +5704,20 @@ drm_reset_display_info(struct drm_connector *connector)
info->mso_pixel_overlap = 0;
}
-u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
+static u32 update_display_info(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
+ const struct edid *edid = drm_edid->edid;
- u32 quirks = edid_get_quirks(edid);
+ u32 quirks = edid_get_quirks(drm_edid);
drm_reset_display_info(connector);
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
- drm_get_monitor_range(connector, edid);
+ drm_get_monitor_range(connector, drm_edid);
if (edid->revision < 3)
goto out;
@@ -5533,7 +5726,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
goto out;
info->color_formats |= DRM_COLOR_FORMAT_RGB444;
- drm_parse_cea_ext(connector, edid);
+ drm_parse_cea_ext(connector, drm_edid);
/*
* Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
@@ -5586,7 +5779,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
- drm_update_mso(connector, edid);
+ drm_update_mso(connector, drm_edid);
out:
if (quirks & EDID_QUIRK_NON_DESKTOP) {
@@ -5598,6 +5791,14 @@ out:
return quirks;
}
+u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
+{
+ struct drm_edid drm_edid;
+
+ return update_display_info(connector,
+ drm_edid_legacy_init(&drm_edid, edid));
+}
+
static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
struct displayid_detailed_timings_1 *timings,
bool type_7)
@@ -5673,13 +5874,13 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
}
static int add_displayid_detailed_modes(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
int num_modes = 0;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
@@ -5691,24 +5892,26 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
}
static int drm_edid_connector_update(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
int num_modes = 0;
u32 quirks;
- if (edid == NULL) {
+ if (!drm_edid) {
+ drm_reset_display_info(connector);
clear_eld(connector);
return 0;
}
- drm_edid_to_eld(connector, edid);
-
/*
* CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
* To avoid multiple parsing of same block, lets parse that map
* from sink info, before parsing CEA modes.
*/
- quirks = drm_add_display_info(connector, edid);
+ quirks = update_display_info(connector, drm_edid);
+
+ /* Depends on info->cea_rev set by update_display_info() above */
+ drm_edid_to_eld(connector, drm_edid);
/*
* EDID spec says modes should be preferred in this order:
@@ -5724,15 +5927,15 @@ static int drm_edid_connector_update(struct drm_connector *connector,
*
* XXX order for additional mode types in extension blocks?
*/
- num_modes += add_detailed_modes(connector, edid, quirks);
- num_modes += add_cvt_modes(connector, edid);
- num_modes += add_standard_modes(connector, edid);
- num_modes += add_established_modes(connector, edid);
- num_modes += add_cea_modes(connector, edid);
- num_modes += add_alternate_cea_modes(connector, edid);
- num_modes += add_displayid_detailed_modes(connector, edid);
- if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
- num_modes += add_inferred_modes(connector, edid);
+ num_modes += add_detailed_modes(connector, drm_edid, quirks);
+ num_modes += add_cvt_modes(connector, drm_edid);
+ num_modes += add_standard_modes(connector, drm_edid);
+ num_modes += add_established_modes(connector, drm_edid);
+ num_modes += add_cea_modes(connector, drm_edid);
+ num_modes += add_alternate_cea_modes(connector, drm_edid);
+ num_modes += add_displayid_detailed_modes(connector, drm_edid);
+ if (drm_edid->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
+ num_modes += add_inferred_modes(connector, drm_edid);
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
edid_fixup_preferred(connector, quirks);
@@ -5765,13 +5968,16 @@ static int drm_edid_connector_update(struct drm_connector *connector,
*/
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
{
+ struct drm_edid drm_edid;
+
if (edid && !drm_edid_is_valid(edid)) {
drm_warn(connector->dev, "%s: EDID invalid.\n",
connector->name);
edid = NULL;
}
- return drm_edid_connector_update(connector, edid);
+ return drm_edid_connector_update(connector,
+ drm_edid_legacy_init(&drm_edid, edid));
}
EXPORT_SYMBOL(drm_add_edid_modes);
@@ -6166,15 +6372,15 @@ static void drm_parse_tiled_block(struct drm_connector *connector,
}
}
-void drm_update_tile_info(struct drm_connector *connector,
- const struct edid *edid)
+static void _drm_update_tile_info(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
connector->has_tile = false;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_TILED_DISPLAY)
drm_parse_tiled_block(connector, block);
@@ -6186,3 +6392,11 @@ void drm_update_tile_info(struct drm_connector *connector,
connector->tile_group = NULL;
}
}
+
+void drm_update_tile_info(struct drm_connector *connector,
+ const struct edid *edid)
+{
+ struct drm_edid drm_edid;
+
+ _drm_update_tile_info(connector, drm_edid_legacy_init(&drm_edid, edid));
+}
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 72e982323a5e..a940024c8087 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -226,7 +226,7 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
container = drmm_kzalloc(dev, size, GFP_KERNEL);
if (!container)
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ENOMEM);
encoder = container + offset;
diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c
index a5026f617739..f16d60217c6c 100644
--- a/drivers/gpu/drm/drm_gem_atomic_helper.c
+++ b/drivers/gpu/drm/drm_gem_atomic_helper.c
@@ -169,8 +169,10 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane,
struct drm_gem_object *obj = drm_gem_fb_get_obj(state->fb, i);
struct dma_fence *new;
- if (WARN_ON_ONCE(!obj))
- continue;
+ if (!obj) {
+ ret = -EINVAL;
+ goto error;
+ }
ret = dma_resv_get_singleton(obj->resv, usage, &new);
if (ret)
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
index f4619803acd0..61339a9cd010 100644
--- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c
+++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
@@ -53,7 +53,11 @@ MODULE_IMPORT_NS(DMA_BUF);
struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
unsigned int plane)
{
- if (plane >= ARRAY_SIZE(fb->obj))
+ struct drm_device *dev = fb->dev;
+
+ if (drm_WARN_ON_ONCE(dev, plane >= ARRAY_SIZE(fb->obj)))
+ return NULL;
+ else if (drm_WARN_ON_ONCE(dev, !fb->obj[plane]))
return NULL;
return fb->obj[plane];
@@ -92,9 +96,9 @@ drm_gem_fb_init(struct drm_device *dev,
*/
void drm_gem_fb_destroy(struct drm_framebuffer *fb)
{
- size_t i;
+ unsigned int i;
- for (i = 0; i < ARRAY_SIZE(fb->obj); i++)
+ for (i = 0; i < fb->format->num_planes; i++)
drm_gem_object_put(fb->obj[i]);
drm_framebuffer_cleanup(fb);
@@ -329,24 +333,26 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
* The argument returns the addresses of the data stored in each BO. This
* is different from @map if the framebuffer's offsets field is non-zero.
*
+ * Both, @map and @data, must each refer to arrays with at least
+ * fb->format->num_planes elements.
+ *
* See drm_gem_fb_vunmap() for unmapping.
*
* Returns:
* 0 on success, or a negative errno code otherwise.
*/
-int drm_gem_fb_vmap(struct drm_framebuffer *fb,
- struct iosys_map map[static DRM_FORMAT_MAX_PLANES],
- struct iosys_map data[DRM_FORMAT_MAX_PLANES])
+int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
+ struct iosys_map *data)
{
struct drm_gem_object *obj;
unsigned int i;
int ret;
- for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) {
+ for (i = 0; i < fb->format->num_planes; ++i) {
obj = drm_gem_fb_get_obj(fb, i);
if (!obj) {
- iosys_map_clear(&map[i]);
- continue;
+ ret = -EINVAL;
+ goto err_drm_gem_vunmap;
}
ret = drm_gem_vmap(obj, &map[i]);
if (ret)
@@ -354,7 +360,7 @@ int drm_gem_fb_vmap(struct drm_framebuffer *fb,
}
if (data) {
- for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) {
+ for (i = 0; i < fb->format->num_planes; ++i) {
memcpy(&data[i], &map[i], sizeof(data[i]));
if (iosys_map_is_null(&data[i]))
continue;
@@ -385,10 +391,9 @@ EXPORT_SYMBOL(drm_gem_fb_vmap);
*
* See drm_gem_fb_vmap() for more information.
*/
-void drm_gem_fb_vunmap(struct drm_framebuffer *fb,
- struct iosys_map map[static DRM_FORMAT_MAX_PLANES])
+void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map)
{
- unsigned int i = DRM_FORMAT_MAX_PLANES;
+ unsigned int i = fb->format->num_planes;
struct drm_gem_object *obj;
while (i) {
@@ -403,6 +408,28 @@ void drm_gem_fb_vunmap(struct drm_framebuffer *fb,
}
EXPORT_SYMBOL(drm_gem_fb_vunmap);
+static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir,
+ unsigned int num_planes)
+{
+ struct dma_buf_attachment *import_attach;
+ struct drm_gem_object *obj;
+ int ret;
+
+ while (num_planes) {
+ --num_planes;
+ obj = drm_gem_fb_get_obj(fb, num_planes);
+ if (!obj)
+ continue;
+ import_attach = obj->import_attach;
+ if (!import_attach)
+ continue;
+ ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
+ if (ret)
+ drm_err(fb->dev, "dma_buf_end_cpu_access(%u, %d) failed: %d\n",
+ ret, num_planes, dir);
+ }
+}
+
/**
* drm_gem_fb_begin_cpu_access - prepares GEM buffer objects for CPU access
* @fb: the framebuffer
@@ -421,40 +448,27 @@ int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direct
{
struct dma_buf_attachment *import_attach;
struct drm_gem_object *obj;
- size_t i;
- int ret, ret2;
+ unsigned int i;
+ int ret;
- for (i = 0; i < ARRAY_SIZE(fb->obj); ++i) {
+ for (i = 0; i < fb->format->num_planes; ++i) {
obj = drm_gem_fb_get_obj(fb, i);
- if (!obj)
- continue;
+ if (!obj) {
+ ret = -EINVAL;
+ goto err___drm_gem_fb_end_cpu_access;
+ }
import_attach = obj->import_attach;
if (!import_attach)
continue;
ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir);
if (ret)
- goto err_dma_buf_end_cpu_access;
+ goto err___drm_gem_fb_end_cpu_access;
}
return 0;
-err_dma_buf_end_cpu_access:
- while (i) {
- --i;
- obj = drm_gem_fb_get_obj(fb, i);
- if (!obj)
- continue;
- import_attach = obj->import_attach;
- if (!import_attach)
- continue;
- ret2 = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
- if (ret2) {
- drm_err(fb->dev,
- "dma_buf_end_cpu_access() failed during error handling: %d\n",
- ret2);
- }
- }
-
+err___drm_gem_fb_end_cpu_access:
+ __drm_gem_fb_end_cpu_access(fb, dir, i);
return ret;
}
EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access);
@@ -472,23 +486,7 @@ EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access);
*/
void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
{
- size_t i = ARRAY_SIZE(fb->obj);
- struct dma_buf_attachment *import_attach;
- struct drm_gem_object *obj;
- int ret;
-
- while (i) {
- --i;
- obj = drm_gem_fb_get_obj(fb, i);
- if (!obj)
- continue;
- import_attach = obj->import_attach;
- if (!import_attach)
- continue;
- ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
- if (ret)
- drm_err(fb->dev, "dma_buf_end_cpu_access() failed: %d\n", ret);
- }
+ __drm_gem_fb_end_cpu_access(fb, dir, fb->format->num_planes);
}
EXPORT_SYMBOL(drm_gem_fb_end_cpu_access);
diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c
index 123045b58fec..d607043716d3 100644
--- a/drivers/gpu/drm/drm_gem_vram_helper.c
+++ b/drivers/gpu/drm/drm_gem_vram_helper.c
@@ -9,6 +9,7 @@
#include <drm/drm_file.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_managed.h>
@@ -630,6 +631,24 @@ EXPORT_SYMBOL(drm_gem_vram_driver_dumb_create);
* Helpers for struct drm_plane_helper_funcs
*/
+static void __drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
+ struct drm_plane_state *state,
+ unsigned int num_planes)
+{
+ struct drm_gem_object *obj;
+ struct drm_gem_vram_object *gbo;
+ struct drm_framebuffer *fb = state->fb;
+
+ while (num_planes) {
+ --num_planes;
+ obj = drm_gem_fb_get_obj(fb, num_planes);
+ if (!obj)
+ continue;
+ gbo = drm_gem_vram_of_gem(obj);
+ drm_gem_vram_unpin(gbo);
+ }
+}
+
/**
* drm_gem_vram_plane_helper_prepare_fb() - \
* Implements &struct drm_plane_helper_funcs.prepare_fb
@@ -648,17 +667,22 @@ int
drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
- size_t i;
+ struct drm_framebuffer *fb = new_state->fb;
struct drm_gem_vram_object *gbo;
+ struct drm_gem_object *obj;
+ unsigned int i;
int ret;
- if (!new_state->fb)
+ if (!fb)
return 0;
- for (i = 0; i < ARRAY_SIZE(new_state->fb->obj); ++i) {
- if (!new_state->fb->obj[i])
- continue;
- gbo = drm_gem_vram_of_gem(new_state->fb->obj[i]);
+ for (i = 0; i < fb->format->num_planes; ++i) {
+ obj = drm_gem_fb_get_obj(fb, i);
+ if (!obj) {
+ ret = -EINVAL;
+ goto err_drm_gem_vram_unpin;
+ }
+ gbo = drm_gem_vram_of_gem(obj);
ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
if (ret)
goto err_drm_gem_vram_unpin;
@@ -671,11 +695,7 @@ drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane,
return 0;
err_drm_gem_vram_unpin:
- while (i) {
- --i;
- gbo = drm_gem_vram_of_gem(new_state->fb->obj[i]);
- drm_gem_vram_unpin(gbo);
- }
+ __drm_gem_vram_plane_helper_cleanup_fb(plane, new_state, i);
return ret;
}
EXPORT_SYMBOL(drm_gem_vram_plane_helper_prepare_fb);
@@ -694,18 +714,12 @@ void
drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
- size_t i;
- struct drm_gem_vram_object *gbo;
+ struct drm_framebuffer *fb = old_state->fb;
- if (!old_state->fb)
+ if (!fb)
return;
- for (i = 0; i < ARRAY_SIZE(old_state->fb->obj); ++i) {
- if (!old_state->fb->obj[i])
- continue;
- gbo = drm_gem_vram_of_gem(old_state->fb->obj[i]);
- drm_gem_vram_unpin(gbo);
- }
+ __drm_gem_vram_plane_helper_cleanup_fb(plane, old_state, fb->format->num_planes);
}
EXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb);
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c
index 9314f2ead79f..09e4edb5a992 100644
--- a/drivers/gpu/drm/drm_mipi_dbi.c
+++ b/drivers/gpu/drm/drm_mipi_dbi.c
@@ -1199,6 +1199,13 @@ int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
size_t chunk;
int ret;
+ /* In __spi_validate, there's a validation that no partial transfers
+ * are accepted (xfer->len % w_size must be zero).
+ * Here we align max_chunk to multiple of 2 (16bits),
+ * to prevent transfers from being rejected.
+ */
+ max_chunk = ALIGN_DOWN(max_chunk, 2);
+
spi_message_init_with_transfers(&m, &tr, 1);
while (len) {
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 14b746f7ba97..40b7b245e98c 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1328,6 +1328,10 @@ void drm_mode_prune_invalid(struct drm_device *dev,
list_for_each_entry_safe(mode, t, mode_list, head) {
if (mode->status != MODE_OK) {
list_del(&mode->head);
+ if (mode->type & DRM_MODE_TYPE_USERDEF) {
+ drm_warn(dev, "User-defined mode not supported: "
+ DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+ }
if (verbose) {
drm_mode_debug_printmodeline(mode);
DRM_DEBUG_KMS("Not using %s mode: %s\n",
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 682359512996..75a71649b64d 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -354,6 +354,61 @@ drm_helper_probe_detect(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_helper_probe_detect);
+static int __drm_helper_update_and_validate(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode;
+ int mode_flags = 0;
+ int ret;
+
+ drm_connector_list_update(connector);
+
+ if (connector->interlace_allowed)
+ mode_flags |= DRM_MODE_FLAG_INTERLACE;
+ if (connector->doublescan_allowed)
+ mode_flags |= DRM_MODE_FLAG_DBLSCAN;
+ if (connector->stereo_allowed)
+ mode_flags |= DRM_MODE_FLAG_3D_MASK;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (mode->status != MODE_OK)
+ continue;
+
+ mode->status = drm_mode_validate_driver(dev, mode);
+ if (mode->status != MODE_OK)
+ continue;
+
+ mode->status = drm_mode_validate_size(mode, maxX, maxY);
+ if (mode->status != MODE_OK)
+ continue;
+
+ mode->status = drm_mode_validate_flag(mode, mode_flags);
+ if (mode->status != MODE_OK)
+ continue;
+
+ ret = drm_mode_validate_pipeline(mode, connector, ctx,
+ &mode->status);
+ if (ret) {
+ drm_dbg_kms(dev,
+ "drm_mode_validate_pipeline failed: %d\n",
+ ret);
+
+ if (drm_WARN_ON_ONCE(dev, ret != -EDEADLK))
+ mode->status = MODE_ERROR;
+ else
+ return -EDEADLK;
+ }
+
+ if (mode->status != MODE_OK)
+ continue;
+ mode->status = drm_mode_validate_ycbcr420(mode, connector);
+ }
+
+ return 0;
+}
+
/**
* drm_helper_probe_single_connector_modes - get complete set of display modes
* @connector: connector to probe
@@ -421,8 +476,6 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
const struct drm_connector_helper_funcs *connector_funcs =
connector->helper_private;
int count = 0, ret;
- int mode_flags = 0;
- bool verbose_prune = true;
enum drm_connector_status old_status;
struct drm_modeset_acquire_ctx ctx;
@@ -502,8 +555,8 @@ retry:
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
connector->base.id, connector->name);
drm_connector_update_edid_property(connector, NULL);
- verbose_prune = false;
- goto prune;
+ drm_mode_prune_invalid(dev, &connector->modes, false);
+ goto exit;
}
count = (*connector_funcs->get_modes)(connector);
@@ -516,60 +569,47 @@ retry:
count = drm_add_override_edid_modes(connector);
if (count == 0 && (connector->status == connector_status_connected ||
- connector->status == connector_status_unknown))
+ connector->status == connector_status_unknown)) {
count = drm_add_modes_noedid(connector, 1024, 768);
- count += drm_helper_probe_add_cmdline_mode(connector);
- if (count == 0)
- goto prune;
-
- drm_connector_list_update(connector);
-
- if (connector->interlace_allowed)
- mode_flags |= DRM_MODE_FLAG_INTERLACE;
- if (connector->doublescan_allowed)
- mode_flags |= DRM_MODE_FLAG_DBLSCAN;
- if (connector->stereo_allowed)
- mode_flags |= DRM_MODE_FLAG_3D_MASK;
-
- list_for_each_entry(mode, &connector->modes, head) {
- if (mode->status != MODE_OK)
- continue;
- mode->status = drm_mode_validate_driver(dev, mode);
- if (mode->status != MODE_OK)
- continue;
-
- mode->status = drm_mode_validate_size(mode, maxX, maxY);
- if (mode->status != MODE_OK)
- continue;
-
- mode->status = drm_mode_validate_flag(mode, mode_flags);
- if (mode->status != MODE_OK)
- continue;
+ /*
+ * Section 4.2.2.6 (EDID Corruption Detection) of the DP 1.4a
+ * Link CTS specifies that 640x480 (the official "failsafe"
+ * mode) needs to be the default if there's no EDID.
+ */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort)
+ drm_set_preferred_mode(connector, 640, 480);
+ }
+ count += drm_helper_probe_add_cmdline_mode(connector);
+ if (count != 0) {
+ ret = __drm_helper_update_and_validate(connector, maxX, maxY, &ctx);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry;
+ }
+ }
- ret = drm_mode_validate_pipeline(mode, connector, &ctx,
- &mode->status);
- if (ret) {
- drm_dbg_kms(dev,
- "drm_mode_validate_pipeline failed: %d\n",
- ret);
+ drm_mode_prune_invalid(dev, &connector->modes, true);
- if (drm_WARN_ON_ONCE(dev, ret != -EDEADLK)) {
- mode->status = MODE_ERROR;
- } else {
- drm_modeset_backoff(&ctx);
- goto retry;
- }
+ /*
+ * Displayport spec section 5.2.1.2 ("Video Timing Format") says that
+ * all detachable sinks shall support 640x480 @60Hz as a fail safe
+ * mode. If all modes were pruned, perhaps because they need more
+ * lanes or a higher pixel clock than available, at least try to add
+ * in 640x480.
+ */
+ if (list_empty(&connector->modes) &&
+ connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+ count = drm_add_modes_noedid(connector, 640, 480);
+ ret = __drm_helper_update_and_validate(connector, maxX, maxY, &ctx);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry;
}
-
- if (mode->status != MODE_OK)
- continue;
- mode->status = drm_mode_validate_ycbcr420(mode, connector);
+ drm_mode_prune_invalid(dev, &connector->modes, true);
}
-prune:
- drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
-
+exit:
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
@@ -964,3 +1004,39 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
return changed;
}
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
+
+/**
+ * drm_connector_helper_get_modes_from_ddc - Updates the connector's EDID
+ * property from the connector's
+ * DDC channel
+ * @connector: The connector
+ *
+ * Returns:
+ * The number of detected display modes.
+ *
+ * Uses a connector's DDC channel to retrieve EDID data and update the
+ * connector's EDID property and display modes. Drivers can use this
+ * function to implement struct &drm_connector_helper_funcs.get_modes
+ * for connectors with a DDC channel.
+ */
+int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector)
+{
+ struct edid *edid;
+ int count = 0;
+
+ if (!connector->ddc)
+ return 0;
+
+ edid = drm_get_edid(connector, connector->ddc);
+
+ // clears property if EDID is NULL
+ drm_connector_update_edid_property(connector, edid);
+
+ if (edid) {
+ count = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(drm_connector_helper_get_modes_from_ddc);
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
index 7e48dcd1bee4..bbad9e4696e7 100644
--- a/drivers/gpu/drm/drm_syncobj.c
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -184,6 +184,7 @@
*/
#include <linux/anon_inodes.h>
+#include <linux/dma-fence-unwrap.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/sched/signal.h>
@@ -853,57 +854,12 @@ drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
&args->handle);
}
-
-/*
- * Try to flatten a dma_fence_chain into a dma_fence_array so that it can be
- * added as timeline fence to a chain again.
- */
-static int drm_syncobj_flatten_chain(struct dma_fence **f)
-{
- struct dma_fence_chain *chain = to_dma_fence_chain(*f);
- struct dma_fence *tmp, **fences;
- struct dma_fence_array *array;
- unsigned int count;
-
- if (!chain)
- return 0;
-
- count = 0;
- dma_fence_chain_for_each(tmp, &chain->base)
- ++count;
-
- fences = kmalloc_array(count, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return -ENOMEM;
-
- count = 0;
- dma_fence_chain_for_each(tmp, &chain->base)
- fences[count++] = dma_fence_get(tmp);
-
- array = dma_fence_array_create(count, fences,
- dma_fence_context_alloc(1),
- 1, false);
- if (!array)
- goto free_fences;
-
- dma_fence_put(*f);
- *f = &array->base;
- return 0;
-
-free_fences:
- while (count--)
- dma_fence_put(fences[count]);
-
- kfree(fences);
- return -ENOMEM;
-}
-
static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
struct drm_syncobj_transfer *args)
{
struct drm_syncobj *timeline_syncobj = NULL;
+ struct dma_fence *fence, *tmp;
struct dma_fence_chain *chain;
- struct dma_fence *fence;
int ret;
timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
@@ -912,13 +868,14 @@ static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
}
ret = drm_syncobj_find_fence(file_private, args->src_handle,
args->src_point, args->flags,
- &fence);
+ &tmp);
if (ret)
goto err_put_timeline;
- ret = drm_syncobj_flatten_chain(&fence);
- if (ret)
- goto err_free_fence;
+ fence = dma_fence_unwrap_merge(tmp);
+ dma_fence_put(tmp);
+ if (!fence)
+ goto err_put_timeline;
chain = dma_fence_chain_alloc();
if (!chain) {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index 6bcd18c63c31..7ff1e5141150 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -192,18 +192,16 @@ static enum drm_connector_status cdv_intel_crt_detect(
static void cdv_intel_crt_destroy(struct drm_connector *connector)
{
struct gma_connector *gma_connector = to_gma_connector(connector);
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
+ struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc);
- psb_intel_i2c_destroy(gma_encoder->ddc_bus);
+ gma_i2c_destroy(ddc_bus);
drm_connector_cleanup(connector);
kfree(gma_connector);
}
static int cdv_intel_crt_get_modes(struct drm_connector *connector)
{
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
- return psb_intel_ddc_get_modes(connector,
- &gma_encoder->ddc_bus->adapter);
+ return psb_intel_ddc_get_modes(connector, connector->ddc);
}
static int cdv_intel_crt_set_property(struct drm_connector *connector,
@@ -245,8 +243,10 @@ void cdv_intel_crt_init(struct drm_device *dev,
struct gma_connector *gma_connector;
struct gma_encoder *gma_encoder;
+ struct gma_i2c_chan *ddc_bus;
struct drm_connector *connector;
struct drm_encoder *encoder;
+ int ret;
gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
if (!gma_encoder)
@@ -254,25 +254,31 @@ void cdv_intel_crt_init(struct drm_device *dev,
gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
if (!gma_connector)
- goto failed_connector;
+ goto err_free_encoder;
+
+ /* Set up the DDC bus. */
+ ddc_bus = gma_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (!ddc_bus) {
+ dev_printk(KERN_ERR, dev->dev, "DDC bus registration failed.\n");
+ goto err_free_connector;
+ }
connector = &gma_connector->base;
connector->polled = DRM_CONNECTOR_POLL_HPD;
- drm_connector_init(dev, connector,
- &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+ ret = drm_connector_init_with_ddc(dev, connector,
+ &cdv_intel_crt_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA,
+ &ddc_bus->base);
+ if (ret)
+ goto err_ddc_destroy;
encoder = &gma_encoder->base;
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC);
+ ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC);
+ if (ret)
+ goto err_connector_cleanup;
gma_connector_attach_encoder(gma_connector, gma_encoder);
- /* Set up the DDC bus. */
- gma_encoder->ddc_bus = psb_intel_i2c_create(dev, GPIOA, "CRTDDC_A");
- if (!gma_encoder->ddc_bus) {
- dev_printk(KERN_ERR, dev->dev, "DDC bus registration failed.\n");
- goto failed_ddc;
- }
-
gma_encoder->type = INTEL_OUTPUT_ANALOG;
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
@@ -282,11 +288,14 @@ void cdv_intel_crt_init(struct drm_device *dev,
&cdv_intel_crt_connector_helper_funcs);
return;
-failed_ddc:
- drm_encoder_cleanup(&gma_encoder->base);
+
+err_connector_cleanup:
drm_connector_cleanup(&gma_connector->base);
+err_ddc_destroy:
+ gma_i2c_destroy(ddc_bus);
+err_free_connector:
kfree(gma_connector);
-failed_connector:
+err_free_encoder:
kfree(gma_encoder);
return;
}
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index 8987e555e113..29ef45f14169 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -53,7 +53,6 @@ struct mid_intel_hdmi_priv {
bool has_hdmi_audio;
/* Should set this when detect hotplug */
bool hdmi_device_connected;
- struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */
struct drm_device *dev;
};
@@ -130,7 +129,7 @@ static enum drm_connector_status cdv_hdmi_detect(
struct edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected;
- edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter);
+ edid = drm_get_edid(connector, connector->ddc);
hdmi_priv->has_hdmi_sink = false;
hdmi_priv->has_hdmi_audio = false;
@@ -208,11 +207,10 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
*/
static int cdv_hdmi_get_modes(struct drm_connector *connector)
{
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
struct edid *edid = NULL;
int ret = 0;
- edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter);
+ edid = drm_get_edid(connector, connector->ddc);
if (edid) {
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
@@ -243,9 +241,9 @@ static enum drm_mode_status cdv_hdmi_mode_valid(struct drm_connector *connector,
static void cdv_hdmi_destroy(struct drm_connector *connector)
{
struct gma_connector *gma_connector = to_gma_connector(connector);
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
+ struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc);
- psb_intel_i2c_destroy(gma_encoder->i2c_bus);
+ gma_i2c_destroy(ddc_bus);
drm_connector_cleanup(connector);
kfree(gma_connector);
}
@@ -278,37 +276,60 @@ void cdv_hdmi_init(struct drm_device *dev,
struct gma_encoder *gma_encoder;
struct gma_connector *gma_connector;
struct drm_connector *connector;
- struct drm_encoder *encoder;
struct mid_intel_hdmi_priv *hdmi_priv;
- int ddc_bus;
+ struct gma_i2c_chan *ddc_bus;
+ int ddc_reg;
+ int ret;
gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
-
if (!gma_encoder)
return;
- gma_connector = kzalloc(sizeof(struct gma_connector),
- GFP_KERNEL);
-
+ gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
if (!gma_connector)
- goto err_connector;
+ goto err_free_encoder;
hdmi_priv = kzalloc(sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL);
-
if (!hdmi_priv)
- goto err_priv;
+ goto err_free_connector;
connector = &gma_connector->base;
connector->polled = DRM_CONNECTOR_POLL_HPD;
gma_connector->save = cdv_hdmi_save;
gma_connector->restore = cdv_hdmi_restore;
- encoder = &gma_encoder->base;
- drm_connector_init(dev, connector,
- &cdv_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_DVID);
+ switch (reg) {
+ case SDVOB:
+ ddc_reg = GPIOE;
+ gma_encoder->ddi_select = DDI0_SELECT;
+ break;
+ case SDVOC:
+ ddc_reg = GPIOD;
+ gma_encoder->ddi_select = DDI1_SELECT;
+ break;
+ default:
+ DRM_ERROR("unknown reg 0x%x for HDMI\n", reg);
+ goto err_free_hdmi_priv;
+ }
+
+ ddc_bus = gma_i2c_create(dev, ddc_reg,
+ (reg == SDVOB) ? "HDMIB" : "HDMIC");
+ if (!ddc_bus) {
+ dev_err(dev->dev, "No ddc adapter available!\n");
+ goto err_free_hdmi_priv;
+ }
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
+ ret = drm_connector_init_with_ddc(dev, connector,
+ &cdv_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_DVID,
+ &ddc_bus->base);
+ if (ret)
+ goto err_ddc_destroy;
+
+ ret = drm_simple_encoder_init(dev, &gma_encoder->base,
+ DRM_MODE_ENCODER_TMDS);
+ if (ret)
+ goto err_connector_cleanup;
gma_connector_attach_encoder(gma_connector, gma_encoder);
gma_encoder->type = INTEL_OUTPUT_HDMI;
@@ -316,7 +337,7 @@ void cdv_hdmi_init(struct drm_device *dev,
hdmi_priv->has_hdmi_sink = false;
gma_encoder->dev_priv = hdmi_priv;
- drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs);
+ drm_encoder_helper_add(&gma_encoder->base, &cdv_hdmi_helper_funcs);
drm_connector_helper_add(connector,
&cdv_hdmi_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
@@ -327,38 +348,17 @@ void cdv_hdmi_init(struct drm_device *dev,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_FULLSCREEN);
- switch (reg) {
- case SDVOB:
- ddc_bus = GPIOE;
- gma_encoder->ddi_select = DDI0_SELECT;
- break;
- case SDVOC:
- ddc_bus = GPIOD;
- gma_encoder->ddi_select = DDI1_SELECT;
- break;
- default:
- DRM_ERROR("unknown reg 0x%x for HDMI\n", reg);
- goto failed_ddc;
- break;
- }
-
- gma_encoder->i2c_bus = psb_intel_i2c_create(dev,
- ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC");
-
- if (!gma_encoder->i2c_bus) {
- dev_err(dev->dev, "No ddc adapter available!\n");
- goto failed_ddc;
- }
-
- hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter);
hdmi_priv->dev = dev;
return;
-failed_ddc:
- drm_encoder_cleanup(encoder);
+err_connector_cleanup:
drm_connector_cleanup(connector);
-err_priv:
+err_ddc_destroy:
+ gma_i2c_destroy(ddc_bus);
+err_free_hdmi_priv:
+ kfree(hdmi_priv);
+err_free_connector:
kfree(gma_connector);
-err_connector:
+err_free_encoder:
kfree(gma_encoder);
}
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 98d9f5483a7c..be6efcaaa3b3 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -298,11 +298,10 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
int ret;
- ret = psb_intel_ddc_get_modes(connector, &gma_encoder->i2c_bus->adapter);
+ ret = psb_intel_ddc_get_modes(connector, connector->ddc);
if (ret)
return ret;
@@ -317,19 +316,13 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector)
return 0;
}
-/**
- * cdv_intel_lvds_destroy - unregister and free LVDS structures
- * @connector: connector to free
- *
- * Unregister the DDC bus for this connector then free the driver private
- * structure.
- */
static void cdv_intel_lvds_destroy(struct drm_connector *connector)
{
struct gma_connector *gma_connector = to_gma_connector(connector);
struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
- psb_intel_i2c_destroy(gma_encoder->i2c_bus);
+ gma_i2c_destroy(to_gma_i2c_chan(connector->ddc));
+ gma_i2c_destroy(gma_encoder->i2c_bus);
drm_connector_cleanup(connector);
kfree(gma_connector);
}
@@ -487,8 +480,10 @@ void cdv_intel_lvds_init(struct drm_device *dev,
struct drm_display_mode *scan;
struct drm_crtc *crtc;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct gma_i2c_chan *ddc_bus;
u32 lvds;
int pipe;
+ int ret;
u8 pin;
if (!dev_priv->lvds_enabled_in_vbt)
@@ -508,11 +503,11 @@ void cdv_intel_lvds_init(struct drm_device *dev,
gma_connector = kzalloc(sizeof(struct gma_connector),
GFP_KERNEL);
if (!gma_connector)
- goto failed_connector;
+ goto err_free_encoder;
lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL);
if (!lvds_priv)
- goto failed_lvds_priv;
+ goto err_free_connector;
gma_encoder->dev_priv = lvds_priv;
@@ -521,12 +516,24 @@ void cdv_intel_lvds_init(struct drm_device *dev,
gma_connector->restore = cdv_intel_lvds_restore;
encoder = &gma_encoder->base;
+ /* Set up the DDC bus. */
+ ddc_bus = gma_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!ddc_bus) {
+ dev_printk(KERN_ERR, dev->dev,
+ "DDC bus registration " "failed.\n");
+ goto err_free_lvds_priv;
+ }
- drm_connector_init(dev, connector,
- &cdv_intel_lvds_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
+ ret = drm_connector_init_with_ddc(dev, connector,
+ &cdv_intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS,
+ &ddc_bus->base);
+ if (ret)
+ goto err_destroy_ddc;
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ if (ret)
+ goto err_connector_cleanup;
gma_connector_attach_encoder(gma_connector, gma_encoder);
gma_encoder->type = INTEL_OUTPUT_LVDS;
@@ -550,13 +557,11 @@ void cdv_intel_lvds_init(struct drm_device *dev,
* Set up I2C bus
* FIXME: distroy i2c_bus when exit
*/
- gma_encoder->i2c_bus = psb_intel_i2c_create(dev,
- GPIOB,
- "LVDSBLC_B");
+ gma_encoder->i2c_bus = gma_i2c_create(dev, GPIOB, "LVDSBLC_B");
if (!gma_encoder->i2c_bus) {
dev_printk(KERN_ERR,
dev->dev, "I2C bus registration failed.\n");
- goto failed_blc_i2c;
+ goto err_encoder_cleanup;
}
gma_encoder->i2c_bus->slave_addr = 0x2C;
dev_priv->lvds_i2c_bus = gma_encoder->i2c_bus;
@@ -571,23 +576,13 @@ void cdv_intel_lvds_init(struct drm_device *dev,
* if closed, act like it's not there for now
*/
- /* Set up the DDC bus. */
- gma_encoder->ddc_bus = psb_intel_i2c_create(dev,
- GPIOC,
- "LVDSDDC_C");
- if (!gma_encoder->ddc_bus) {
- dev_printk(KERN_ERR, dev->dev,
- "DDC bus registration " "failed.\n");
- goto failed_ddc;
- }
-
/*
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
mutex_lock(&dev->mode_config.mutex);
- psb_intel_ddc_get_modes(connector,
- &gma_encoder->ddc_bus->adapter);
+ psb_intel_ddc_get_modes(connector, &ddc_bus->base);
+
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
mode_dev->panel_fixed_mode =
@@ -629,7 +624,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
if (!mode_dev->panel_fixed_mode) {
DRM_DEBUG
("Found no modes on the lvds, ignoring the LVDS\n");
- goto failed_find;
+ goto err_unlock;
}
/* setup PWM */
@@ -649,20 +644,19 @@ out:
mutex_unlock(&dev->mode_config.mutex);
return;
-failed_find:
+err_unlock:
mutex_unlock(&dev->mode_config.mutex);
- pr_err("Failed find\n");
- psb_intel_i2c_destroy(gma_encoder->ddc_bus);
-failed_ddc:
- pr_err("Failed DDC\n");
- psb_intel_i2c_destroy(gma_encoder->i2c_bus);
-failed_blc_i2c:
- pr_err("Failed BLC\n");
+ gma_i2c_destroy(gma_encoder->i2c_bus);
+err_encoder_cleanup:
drm_encoder_cleanup(encoder);
+err_connector_cleanup:
drm_connector_cleanup(connector);
+err_destroy_ddc:
+ gma_i2c_destroy(ddc_bus);
+err_free_lvds_priv:
kfree(lvds_priv);
-failed_lvds_priv:
+err_free_connector:
kfree(gma_connector);
-failed_connector:
+err_free_encoder:
kfree(gma_encoder);
}
diff --git a/drivers/gpu/drm/gma500/intel_i2c.c b/drivers/gpu/drm/gma500/intel_i2c.c
index 5e1b4d70c317..9d02a7b6d9a3 100644
--- a/drivers/gpu/drm/gma500/intel_i2c.c
+++ b/drivers/gpu/drm/gma500/intel_i2c.c
@@ -22,7 +22,7 @@
static int get_clock(void *data)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
struct drm_device *dev = chan->drm_dev;
u32 val;
@@ -32,7 +32,7 @@ static int get_clock(void *data)
static int get_data(void *data)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
struct drm_device *dev = chan->drm_dev;
u32 val;
@@ -42,7 +42,7 @@ static int get_data(void *data)
static void set_clock(void *data, int state_high)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
struct drm_device *dev = chan->drm_dev;
u32 reserved = 0, clock_bits;
@@ -62,7 +62,7 @@ static void set_clock(void *data, int state_high)
static void set_data(void *data, int state_high)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
struct drm_device *dev = chan->drm_dev;
u32 reserved = 0, data_bits;
@@ -83,7 +83,7 @@ static void set_data(void *data, int state_high)
}
/**
- * psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * gma_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
* @dev: DRM device
* @reg: GPIO reg to use
* @name: name for this bus
@@ -102,21 +102,21 @@ static void set_data(void *data, int state_high)
* %GPIOH
* see PRM for details on how these different busses are used.
*/
-struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
- const u32 reg, const char *name)
+struct gma_i2c_chan *gma_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name)
{
- struct psb_intel_i2c_chan *chan;
+ struct gma_i2c_chan *chan;
- chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL);
+ chan = kzalloc(sizeof(struct gma_i2c_chan), GFP_KERNEL);
if (!chan)
goto out_free;
chan->drm_dev = dev;
chan->reg = reg;
- snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
- chan->adapter.owner = THIS_MODULE;
- chan->adapter.algo_data = &chan->algo;
- chan->adapter.dev.parent = dev->dev;
+ snprintf(chan->base.name, I2C_NAME_SIZE, "intel drm %s", name);
+ chan->base.owner = THIS_MODULE;
+ chan->base.algo_data = &chan->algo;
+ chan->base.dev.parent = dev->dev;
chan->algo.setsda = set_data;
chan->algo.setscl = set_clock;
chan->algo.getsda = get_data;
@@ -125,9 +125,9 @@ struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
chan->algo.timeout = usecs_to_jiffies(2200);
chan->algo.data = chan;
- i2c_set_adapdata(&chan->adapter, chan);
+ i2c_set_adapdata(&chan->base, chan);
- if (i2c_bit_add_bus(&chan->adapter))
+ if (i2c_bit_add_bus(&chan->base))
goto out_free;
/* JJJ: raise SCL and SDA? */
@@ -143,16 +143,16 @@ out_free:
}
/**
- * psb_intel_i2c_destroy - unregister and free i2c bus resources
+ * gma_i2c_destroy - unregister and free i2c bus resources
* @chan: channel to free
*
* Unregister the adapter from the i2c layer, then free the structure.
*/
-void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan)
+void gma_i2c_destroy(struct gma_i2c_chan *chan)
{
if (!chan)
return;
- i2c_del_adapter(&chan->adapter);
+ i2c_del_adapter(&chan->base);
kfree(chan);
}
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index aed5de8f8245..9c9ebf8e29c4 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -293,12 +293,14 @@ void oaktrail_lvds_init(struct drm_device *dev,
{
struct gma_encoder *gma_encoder;
struct gma_connector *gma_connector;
+ struct gma_i2c_chan *ddc_bus;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
struct edid *edid;
struct i2c_adapter *i2c_adap;
struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ int ret;
gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
if (!gma_encoder)
@@ -306,16 +308,20 @@ void oaktrail_lvds_init(struct drm_device *dev,
gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
if (!gma_connector)
- goto failed_connector;
+ goto err_free_encoder;
connector = &gma_connector->base;
encoder = &gma_encoder->base;
dev_priv->is_lvds_on = true;
- drm_connector_init(dev, connector,
- &psb_intel_lvds_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
+ ret = drm_connector_init(dev, connector,
+ &psb_intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ if (ret)
+ goto err_free_connector;
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ if (ret)
+ goto err_connector_cleanup;
gma_connector_attach_encoder(gma_connector, gma_encoder);
gma_encoder->type = INTEL_OUTPUT_LVDS;
@@ -353,16 +359,26 @@ void oaktrail_lvds_init(struct drm_device *dev,
edid = NULL;
mutex_lock(&dev->mode_config.mutex);
+
i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
if (i2c_adap)
edid = drm_get_edid(connector, i2c_adap);
+
if (edid == NULL && dev_priv->lpc_gpio_base) {
- oaktrail_lvds_i2c_init(encoder);
- if (gma_encoder->ddc_bus != NULL) {
- i2c_adap = &gma_encoder->ddc_bus->adapter;
+ ddc_bus = oaktrail_lvds_i2c_init(dev);
+ if (!IS_ERR(ddc_bus)) {
+ i2c_adap = &ddc_bus->base;
edid = drm_get_edid(connector, i2c_adap);
}
}
+
+ /*
+ * Due to the logic in probing for i2c buses above we do not know the
+ * i2c_adap until now. Hence we cannot use drm_connector_init_with_ddc()
+ * but must instead set connector->ddc manually here.
+ */
+ connector->ddc = i2c_adap;
+
/*
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
@@ -395,7 +411,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
/* If we still don't have a mode after all that, give up. */
if (!mode_dev->panel_fixed_mode) {
dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
- goto failed_find;
+ goto err_unlock;
}
out:
@@ -403,21 +419,15 @@ out:
return;
-failed_find:
+err_unlock:
mutex_unlock(&dev->mode_config.mutex);
-
- dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
- if (gma_encoder->ddc_bus) {
- psb_intel_i2c_destroy(gma_encoder->ddc_bus);
- gma_encoder->ddc_bus = NULL;
- }
-
-/* failed_ddc: */
-
+ gma_i2c_destroy(to_gma_i2c_chan(connector->ddc));
drm_encoder_cleanup(encoder);
+err_connector_cleanup:
drm_connector_cleanup(connector);
+err_free_connector:
kfree(gma_connector);
-failed_connector:
+err_free_encoder:
kfree(gma_encoder);
}
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c
index d1ae91fcd224..06b5b2d70d48 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c
@@ -65,7 +65,7 @@
static int get_clock(void *data)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
u32 val;
val = LPC_READ_REG(chan, RGIO);
@@ -79,7 +79,7 @@ static int get_clock(void *data)
static int get_data(void *data)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
u32 val;
val = LPC_READ_REG(chan, RGIO);
@@ -93,7 +93,7 @@ static int get_data(void *data)
static void set_clock(void *data, int state_high)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
u32 val;
if (state_high) {
@@ -112,7 +112,7 @@ static void set_clock(void *data, int state_high)
static void set_data(void *data, int state_high)
{
- struct psb_intel_i2c_chan *chan = data;
+ struct gma_i2c_chan *chan = data;
u32 val;
if (state_high) {
@@ -129,23 +129,22 @@ static void set_data(void *data, int state_high)
}
}
-void oaktrail_lvds_i2c_init(struct drm_encoder *encoder)
+struct gma_i2c_chan *oaktrail_lvds_i2c_init(struct drm_device *dev)
{
- struct drm_device *dev = encoder->dev;
- struct gma_encoder *gma_encoder = to_gma_encoder(encoder);
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
- struct psb_intel_i2c_chan *chan;
+ struct gma_i2c_chan *chan;
+ int ret;
- chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL);
+ chan = kzalloc(sizeof(struct gma_i2c_chan), GFP_KERNEL);
if (!chan)
- return;
+ return ERR_PTR(-ENOMEM);
chan->drm_dev = dev;
chan->reg = dev_priv->lpc_gpio_base;
- strncpy(chan->adapter.name, "gma500 LPC", I2C_NAME_SIZE - 1);
- chan->adapter.owner = THIS_MODULE;
- chan->adapter.algo_data = &chan->algo;
- chan->adapter.dev.parent = dev->dev;
+ strncpy(chan->base.name, "gma500 LPC", I2C_NAME_SIZE - 1);
+ chan->base.owner = THIS_MODULE;
+ chan->base.algo_data = &chan->algo;
+ chan->base.dev.parent = dev->dev;
chan->algo.setsda = set_data;
chan->algo.setscl = set_clock;
chan->algo.getsda = get_data;
@@ -154,16 +153,17 @@ void oaktrail_lvds_i2c_init(struct drm_encoder *encoder)
chan->algo.timeout = usecs_to_jiffies(2200);
chan->algo.data = chan;
- i2c_set_adapdata(&chan->adapter, chan);
+ i2c_set_adapdata(&chan->base, chan);
set_data(chan, 1);
set_clock(chan, 1);
udelay(50);
- if (i2c_bit_add_bus(&chan->adapter)) {
+ ret = i2c_bit_add_bus(&chan->base);
+ if (ret < 0) {
kfree(chan);
- return;
+ return ERR_PTR(ret);
}
- gma_encoder->ddc_bus = chan;
+ return chan;
}
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index 0ddfec1a0851..0ea3d23575f3 100644
--- a/drivers/gpu/drm/gma500/psb_drv.h
+++ b/drivers/gpu/drm/gma500/psb_drv.h
@@ -469,7 +469,7 @@ struct drm_psb_private {
struct drm_display_mode *sdvo_lvds_vbt_mode;
struct bdb_lvds_backlight *lvds_bl; /* LVDS backlight info from VBT */
- struct psb_intel_i2c_chan *lvds_i2c_bus; /* FIXME: Remove this? */
+ struct gma_i2c_chan *lvds_i2c_bus; /* FIXME: Remove this? */
/* Feature bits from the VBIOS */
unsigned int int_tv_support:1;
diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h
index db3e757328fe..8ccba116821b 100644
--- a/drivers/gpu/drm/gma500/psb_intel_drv.h
+++ b/drivers/gpu/drm/gma500/psb_intel_drv.h
@@ -78,13 +78,14 @@ struct psb_intel_mode_device {
uint32_t saveBLC_PWM_CTL;
};
-struct psb_intel_i2c_chan {
- /* for getting at dev. private (mmio etc.) */
- struct drm_device *drm_dev;
- u32 reg; /* GPIO reg */
- struct i2c_adapter adapter;
+struct gma_i2c_chan {
+ struct i2c_adapter base;
struct i2c_algo_bit_data algo;
u8 slave_addr;
+
+ /* for getting at dev. private (mmio etc.) */
+ struct drm_device *drm_dev;
+ u32 reg; /* GPIO reg */
};
struct gma_encoder {
@@ -103,8 +104,7 @@ struct gma_encoder {
/* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's
own set of output privates */
- struct psb_intel_i2c_chan *i2c_bus;
- struct psb_intel_i2c_chan *ddc_bus;
+ struct gma_i2c_chan *i2c_bus;
};
struct gma_connector {
@@ -175,10 +175,12 @@ struct gma_crtc {
container_of(x, struct gma_encoder, base)
#define to_psb_intel_framebuffer(x) \
container_of(x, struct psb_intel_framebuffer, base)
+#define to_gma_i2c_chan(x) \
+ container_of(x, struct gma_i2c_chan, base)
-struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
- const u32 reg, const char *name);
-void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan);
+struct gma_i2c_chan *gma_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name);
+void gma_i2c_destroy(struct gma_i2c_chan *chan);
int psb_intel_ddc_get_modes(struct drm_connector *connector,
struct i2c_adapter *adapter);
extern bool psb_intel_ddc_probe(struct i2c_adapter *adapter);
@@ -197,7 +199,7 @@ extern void oaktrail_lvds_init(struct drm_device *dev,
extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev);
extern void oaktrail_dsi_init(struct drm_device *dev,
struct psb_intel_mode_device *mode_dev);
-extern void oaktrail_lvds_i2c_init(struct drm_encoder *encoder);
+struct gma_i2c_chan *oaktrail_lvds_i2c_init(struct drm_device *dev);
extern void mid_dsi_init(struct drm_device *dev,
struct psb_intel_mode_device *mode_dev, int dsi_num);
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index cad00380b386..7ee6c8ce103b 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -49,8 +49,7 @@ struct psb_intel_lvds_priv {
uint32_t savePFIT_PGM_RATIOS;
uint32_t saveBLC_PWM_CTL;
- struct psb_intel_i2c_chan *i2c_bus;
- struct psb_intel_i2c_chan *ddc_bus;
+ struct gma_i2c_chan *i2c_bus;
};
@@ -90,7 +89,7 @@ static int psb_lvds_i2c_set_brightness(struct drm_device *dev,
{
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
- struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus;
+ struct gma_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus;
u8 out_buf[2];
unsigned int blc_i2c_brightness;
@@ -113,7 +112,7 @@ static int psb_lvds_i2c_set_brightness(struct drm_device *dev,
out_buf[0] = dev_priv->lvds_bl->brightnesscmd;
out_buf[1] = (u8)blc_i2c_brightness;
- if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) {
+ if (i2c_transfer(&lvds_i2c_bus->base, msgs, 1) == 1) {
dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n",
dev_priv->lvds_bl->brightnesscmd,
blc_i2c_brightness);
@@ -492,12 +491,10 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
- struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv;
int ret = 0;
if (!IS_MRST(dev))
- ret = psb_intel_ddc_get_modes(connector, &lvds_priv->i2c_bus->adapter);
+ ret = psb_intel_ddc_get_modes(connector, connector->ddc);
if (ret)
return ret;
@@ -512,20 +509,12 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector)
return 0;
}
-/**
- * psb_intel_lvds_destroy - unregister and free LVDS structures
- * @connector: connector to free
- *
- * Unregister the DDC bus for this connector then free the driver private
- * structure.
- */
void psb_intel_lvds_destroy(struct drm_connector *connector)
{
struct gma_connector *gma_connector = to_gma_connector(connector);
- struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
- struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv;
+ struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc);
- psb_intel_i2c_destroy(lvds_priv->ddc_bus);
+ gma_i2c_destroy(ddc_bus);
drm_connector_cleanup(connector);
kfree(gma_connector);
}
@@ -639,25 +628,28 @@ void psb_intel_lvds_init(struct drm_device *dev,
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_crtc *crtc;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct gma_i2c_chan *ddc_bus;
u32 lvds;
int pipe;
+ int ret;
gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
if (!gma_encoder) {
dev_err(dev->dev, "gma_encoder allocation error\n");
return;
}
+ encoder = &gma_encoder->base;
gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
if (!gma_connector) {
dev_err(dev->dev, "gma_connector allocation error\n");
- goto failed_encoder;
+ goto err_free_encoder;
}
lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL);
if (!lvds_priv) {
dev_err(dev->dev, "LVDS private allocation error\n");
- goto failed_connector;
+ goto err_free_connector;
}
gma_encoder->dev_priv = lvds_priv;
@@ -666,12 +658,24 @@ void psb_intel_lvds_init(struct drm_device *dev,
gma_connector->save = psb_intel_lvds_save;
gma_connector->restore = psb_intel_lvds_restore;
- encoder = &gma_encoder->base;
- drm_connector_init(dev, connector,
- &psb_intel_lvds_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
+ /* Set up the DDC bus. */
+ ddc_bus = gma_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!ddc_bus) {
+ dev_printk(KERN_ERR, dev->dev,
+ "DDC bus registration " "failed.\n");
+ goto err_free_lvds_priv;
+ }
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_connector_init_with_ddc(dev, connector,
+ &psb_intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS,
+ &ddc_bus->base);
+ if (ret)
+ goto err_ddc_destroy;
+
+ ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ if (ret)
+ goto err_connector_cleanup;
gma_connector_attach_encoder(gma_connector, gma_encoder);
gma_encoder->type = INTEL_OUTPUT_LVDS;
@@ -695,11 +699,11 @@ void psb_intel_lvds_init(struct drm_device *dev,
* Set up I2C bus
* FIXME: distroy i2c_bus when exit
*/
- lvds_priv->i2c_bus = psb_intel_i2c_create(dev, GPIOB, "LVDSBLC_B");
+ lvds_priv->i2c_bus = gma_i2c_create(dev, GPIOB, "LVDSBLC_B");
if (!lvds_priv->i2c_bus) {
dev_printk(KERN_ERR,
dev->dev, "I2C bus registration failed.\n");
- goto failed_blc_i2c;
+ goto err_encoder_cleanup;
}
lvds_priv->i2c_bus->slave_addr = 0x2C;
dev_priv->lvds_i2c_bus = lvds_priv->i2c_bus;
@@ -714,20 +718,13 @@ void psb_intel_lvds_init(struct drm_device *dev,
* if closed, act like it's not there for now
*/
- /* Set up the DDC bus. */
- lvds_priv->ddc_bus = psb_intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
- if (!lvds_priv->ddc_bus) {
- dev_printk(KERN_ERR, dev->dev,
- "DDC bus registration " "failed.\n");
- goto failed_ddc;
- }
-
/*
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
mutex_lock(&dev->mode_config.mutex);
- psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter);
+ psb_intel_ddc_get_modes(connector, &ddc_bus->base);
+
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
mode_dev->panel_fixed_mode =
@@ -773,7 +770,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
/* If we still don't have a mode after all that, give up. */
if (!mode_dev->panel_fixed_mode) {
dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
- goto failed_find;
+ goto err_unlock;
}
/*
@@ -784,17 +781,20 @@ out:
mutex_unlock(&dev->mode_config.mutex);
return;
-failed_find:
+err_unlock:
mutex_unlock(&dev->mode_config.mutex);
- psb_intel_i2c_destroy(lvds_priv->ddc_bus);
-failed_ddc:
- psb_intel_i2c_destroy(lvds_priv->i2c_bus);
-failed_blc_i2c:
+ gma_i2c_destroy(lvds_priv->i2c_bus);
+err_encoder_cleanup:
drm_encoder_cleanup(encoder);
+err_connector_cleanup:
drm_connector_cleanup(connector);
-failed_connector:
+err_ddc_destroy:
+ gma_i2c_destroy(ddc_bus);
+err_free_lvds_priv:
+ kfree(lvds_priv);
+err_free_connector:
kfree(gma_connector);
-failed_encoder:
+err_free_encoder:
kfree(gma_encoder);
}
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 4a8941fa0815..6d11e7938c83 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -69,56 +69,7 @@ static struct pci_driver hyperv_pci_driver = {
.remove = hyperv_pci_remove,
};
-static int hyperv_setup_gen1(struct hyperv_drm_device *hv)
-{
- struct drm_device *dev = &hv->dev;
- struct pci_dev *pdev;
- int ret;
-
- pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
- PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
- if (!pdev) {
- drm_err(dev, "Unable to find PCI Hyper-V video\n");
- return -ENODEV;
- }
-
- ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &hyperv_driver);
- if (ret) {
- drm_err(dev, "Not able to remove boot fb\n");
- return ret;
- }
-
- if (pci_request_region(pdev, 0, DRIVER_NAME) != 0)
- drm_warn(dev, "Cannot request framebuffer, boot fb still active?\n");
-
- if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) {
- drm_err(dev, "Resource at bar 0 is not IORESOURCE_MEM\n");
- ret = -ENODEV;
- goto error;
- }
-
- hv->fb_base = pci_resource_start(pdev, 0);
- hv->fb_size = pci_resource_len(pdev, 0);
- if (!hv->fb_base) {
- drm_err(dev, "Resource not available\n");
- ret = -ENODEV;
- goto error;
- }
-
- hv->fb_size = min(hv->fb_size,
- (unsigned long)(hv->mmio_megabytes * 1024 * 1024));
- hv->vram = devm_ioremap(&pdev->dev, hv->fb_base, hv->fb_size);
- if (!hv->vram) {
- drm_err(dev, "Failed to map vram\n");
- ret = -ENOMEM;
- }
-
-error:
- pci_dev_put(pdev);
- return ret;
-}
-
-static int hyperv_setup_gen2(struct hyperv_drm_device *hv,
+static int hyperv_setup_vram(struct hyperv_drm_device *hv,
struct hv_device *hdev)
{
struct drm_device *dev = &hv->dev;
@@ -181,10 +132,7 @@ static int hyperv_vmbus_probe(struct hv_device *hdev,
goto err_hv_set_drv_data;
}
- if (efi_enabled(EFI_BOOT))
- ret = hyperv_setup_gen2(hv, hdev);
- else
- ret = hyperv_setup_gen1(hv);
+ ret = hyperv_setup_vram(hv, hdev);
if (ret)
goto err_vmbus_close;
@@ -225,29 +173,13 @@ static int hyperv_vmbus_remove(struct hv_device *hdev)
{
struct drm_device *dev = hv_get_drvdata(hdev);
struct hyperv_drm_device *hv = to_hv(dev);
- struct pci_dev *pdev;
drm_dev_unplug(dev);
drm_atomic_helper_shutdown(dev);
vmbus_close(hdev->channel);
hv_set_drvdata(hdev, NULL);
- /*
- * Free allocated MMIO memory only on Gen2 VMs.
- * On Gen1 VMs, release the PCI device
- */
- if (efi_enabled(EFI_BOOT)) {
- vmbus_free_mmio(hv->mem->start, hv->fb_size);
- } else {
- pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
- PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
- if (!pdev) {
- drm_err(dev, "Unable to find PCI Hyper-V video\n");
- return -ENODEV;
- }
- pci_release_region(pdev, 0);
- pci_dev_put(pdev);
- }
+ vmbus_free_mmio(hv->mem->start, hv->fb_size);
return 0;
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index 452d773fd4e3..6c3954479047 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -590,6 +590,8 @@ static void intel_connector_info(struct seq_file *m,
seq_puts(m, "\tHDCP version: ");
intel_hdcp_info(m, intel_connector);
+ seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc);
+
intel_panel_info(m, intel_connector);
seq_printf(m, "\tmodes:\n");
@@ -2202,6 +2204,29 @@ static const struct file_operations i915_dsc_bpp_fops = {
.write = i915_dsc_bpp_write
};
+/*
+ * Returns the Current CRTC's bpc.
+ * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/i915_current_bpc
+ */
+static int i915_current_bpc_show(struct seq_file *m, void *data)
+{
+ struct intel_crtc *crtc = to_intel_crtc(m->private);
+ struct intel_crtc_state *crtc_state;
+ int ret;
+
+ ret = drm_modeset_lock_single_interruptible(&crtc->base.mutex);
+ if (ret)
+ return ret;
+
+ crtc_state = to_intel_crtc_state(crtc->base.state);
+ seq_printf(m, "Current: %u\n", crtc_state->pipe_bpp / 3);
+
+ drm_modeset_unlock(&crtc->base.mutex);
+
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(i915_current_bpc);
+
/**
* intel_connector_debugfs_add - add i915 specific connector debugfs files
* @connector: pointer to a registered drm_connector
@@ -2272,4 +2297,7 @@ void intel_crtc_debugfs_add(struct drm_crtc *crtc)
crtc_updates_add(crtc);
intel_fbc_crtc_debugfs_add(to_intel_crtc(crtc));
+
+ debugfs_create_file("i915_current_bpc", 0444, crtc->debugfs_entry, crtc,
+ &i915_current_bpc_fops);
}
diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
index fd8db97ba8ba..8110a6e39320 100644
--- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c
+++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
@@ -238,6 +238,7 @@ int meson_encoder_cvbs_init(struct meson_drm *priv)
}
meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
+ of_node_put(remote);
if (!meson_encoder_cvbs->next_bridge) {
dev_err(priv->dev, "Failed to find CVBS Connector bridge\n");
return -EPROBE_DEFER;
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
index 5e306de6f485..2f616c55c271 100644
--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -218,7 +218,8 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge,
if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
yuv420_mode = true;
- }
+ } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16)
+ ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
/* VENC + VENC-DVI Mode setup */
meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);
@@ -230,6 +231,10 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge,
/* Setup YUV420 to HDMI-TX, no 10bit diphering */
writel_relaxed(2 | (2 << 2),
priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+ else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16)
+ /* Setup YUV422 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(1 | (2 << 2),
+ priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
else
/* Setup YUV444 to HDMI-TX, no 10bit diphering */
writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
@@ -257,6 +262,7 @@ static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge,
static const u32 meson_encoder_hdmi_out_bus_fmts[] = {
MEDIA_BUS_FMT_YUV8_1X24,
+ MEDIA_BUS_FMT_UYVY8_1X16,
MEDIA_BUS_FMT_UYYVYY8_0_5X24,
};
@@ -365,7 +371,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
if (!meson_encoder_hdmi->next_bridge) {
dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n");
- return -EPROBE_DEFER;
+ ret = -EPROBE_DEFER;
+ goto err_put_node;
}
/* HDMI Encoder Bridge */
@@ -383,7 +390,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
DRM_MODE_ENCODER_TMDS);
if (ret) {
dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret);
- return ret;
+ goto err_put_node;
}
meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
@@ -393,7 +400,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
- return ret;
+ goto err_put_node;
}
/* Initialize & attach Bridge Connector */
@@ -401,7 +408,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
&meson_encoder_hdmi->encoder);
if (IS_ERR(meson_encoder_hdmi->connector)) {
dev_err(priv->dev, "Unable to create HDMI bridge connector\n");
- return PTR_ERR(meson_encoder_hdmi->connector);
+ ret = PTR_ERR(meson_encoder_hdmi->connector);
+ goto err_put_node;
}
drm_connector_attach_encoder(meson_encoder_hdmi->connector,
&meson_encoder_hdmi->encoder);
@@ -428,6 +436,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
meson_encoder_hdmi->connector->ycbcr_420_allowed = true;
pdev = of_find_device_by_node(remote);
+ of_node_put(remote);
if (pdev) {
struct cec_connector_info conn_info;
struct cec_notifier *notifier;
@@ -435,8 +444,10 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector);
notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info);
- if (!notifier)
+ if (!notifier) {
+ put_device(&pdev->dev);
return -ENOMEM;
+ }
meson_encoder_hdmi->cec_notifier = notifier;
}
@@ -444,4 +455,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
dev_dbg(priv->dev, "HDMI encoder initialized\n");
return 0;
+
+err_put_node:
+ of_node_put(remote);
+ return ret;
}
diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile
index d4f766522483..89558549c3af 100644
--- a/drivers/gpu/drm/mgag200/Makefile
+++ b/drivers/gpu/drm/mgag200/Makefile
@@ -1,4 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
-mgag200-y := mgag200_drv.o mgag200_i2c.o mgag200_mm.o mgag200_mode.o mgag200_pll.o
+mgag200-y := \
+ mgag200_drv.o \
+ mgag200_g200.o \
+ mgag200_g200eh.o \
+ mgag200_g200eh3.o \
+ mgag200_g200er.o \
+ mgag200_g200ev.o \
+ mgag200_g200ew3.o \
+ mgag200_g200se.o \
+ mgag200_g200wb.o \
+ mgag200_i2c.o \
+ mgag200_mode.o \
+ mgag200_pll.o
obj-$(CONFIG_DRM_MGAG200) += mgag200.o
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 08839460606f..361eb7dffda1 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -24,6 +24,71 @@ int mgag200_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, mgag200_modeset, int, 0400);
+int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2)
+{
+ struct device *dev = &pdev->dev;
+ int err;
+
+ err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
+ if (err != PCIBIOS_SUCCESSFUL) {
+ dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
+ return pcibios_err_to_errno(err);
+ }
+
+ err = pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
+ if (err != PCIBIOS_SUCCESSFUL) {
+ dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
+ return pcibios_err_to_errno(err);
+ }
+
+ err = pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2);
+ if (err != PCIBIOS_SUCCESSFUL) {
+ dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION2) failed: %d\n", err);
+ return pcibios_err_to_errno(err);
+ }
+
+ return 0;
+}
+
+resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size)
+{
+ int offset;
+ int orig;
+ int test1, test2;
+ int orig1, orig2;
+ size_t vram_size;
+
+ /* Probe */
+ orig = ioread16(mem);
+ iowrite16(0, mem);
+
+ vram_size = size;
+
+ for (offset = 0x100000; offset < vram_size; offset += 0x4000) {
+ orig1 = ioread8(mem + offset);
+ orig2 = ioread8(mem + offset + 0x100);
+
+ iowrite16(0xaa55, mem + offset);
+ iowrite16(0xaa55, mem + offset + 0x100);
+
+ test1 = ioread16(mem + offset);
+ test2 = ioread16(mem);
+
+ iowrite16(orig1, mem + offset);
+ iowrite16(orig2, mem + offset + 0x100);
+
+ if (test1 != 0xaa55)
+ break;
+
+ if (test2)
+ break;
+ }
+
+ iowrite16(orig, mem);
+
+ return offset - 65536;
+}
+
/*
* DRM driver
*/
@@ -46,263 +111,87 @@ static const struct drm_driver mgag200_driver = {
* DRM device
*/
-static bool mgag200_has_sgram(struct mga_device *mdev)
+resource_size_t mgag200_device_probe_vram(struct mga_device *mdev)
{
- struct drm_device *dev = &mdev->base;
- struct pci_dev *pdev = to_pci_dev(dev->dev);
- u32 option;
- int ret;
-
- ret = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
- if (drm_WARN(dev, ret, "failed to read PCI config dword: %d\n", ret))
- return false;
-
- return !!(option & PCI_MGA_OPTION_HARDPWMSK);
+ return mgag200_probe_vram(mdev->vram, resource_size(mdev->vram_res));
}
-static int mgag200_regs_init(struct mga_device *mdev)
+int mgag200_device_preinit(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct pci_dev *pdev = to_pci_dev(dev->dev);
- u32 option, option2;
- u8 crtcext3;
- int ret;
-
- ret = drmm_mutex_init(dev, &mdev->rmmio_lock);
- if (ret)
- return ret;
-
- switch (mdev->type) {
- case G200_PCI:
- case G200_AGP:
- if (mgag200_has_sgram(mdev))
- option = 0x4049cd21;
- else
- option = 0x40499121;
- option2 = 0x00008000;
- break;
- case G200_SE_A:
- case G200_SE_B:
- option = 0x40049120;
- if (mgag200_has_sgram(mdev))
- option |= PCI_MGA_OPTION_HARDPWMSK;
- option2 = 0x00008000;
- break;
- case G200_WB:
- case G200_EW3:
- option = 0x41049120;
- option2 = 0x0000b000;
- break;
- case G200_EV:
- option = 0x00000120;
- option2 = 0x0000b000;
- break;
- case G200_EH:
- case G200_EH3:
- option = 0x00000120;
- option2 = 0x0000b000;
- break;
- default:
- option = 0;
- option2 = 0;
- }
-
- if (option)
- pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
- if (option2)
- pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2);
+ resource_size_t start, len;
+ struct resource *res;
/* BAR 1 contains registers */
- mdev->rmmio_base = pci_resource_start(pdev, 1);
- mdev->rmmio_size = pci_resource_len(pdev, 1);
- if (!devm_request_mem_region(dev->dev, mdev->rmmio_base,
- mdev->rmmio_size, "mgadrmfb_mmio")) {
- drm_err(dev, "can't reserve mmio registers\n");
- return -ENOMEM;
+ start = pci_resource_start(pdev, 1);
+ len = pci_resource_len(pdev, 1);
+
+ res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_mmio");
+ if (!res) {
+ drm_err(dev, "devm_request_mem_region(MMIO) failed\n");
+ return -ENXIO;
}
+ mdev->rmmio_res = res;
mdev->rmmio = pcim_iomap(pdev, 1, 0);
- if (mdev->rmmio == NULL)
+ if (!mdev->rmmio)
return -ENOMEM;
- RREG_ECRT(0x03, crtcext3);
- crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
- WREG_ECRT(0x03, crtcext3);
-
- return 0;
-}
-
-static void mgag200_g200_interpret_bios(struct mga_device *mdev,
- const unsigned char *bios,
- size_t size)
-{
- static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'};
- static const unsigned int expected_length[6] = {
- 0, 64, 64, 64, 128, 128
- };
- struct drm_device *dev = &mdev->base;
- const unsigned char *pins;
- unsigned int pins_len, version;
- int offset;
- int tmp;
-
- /* Test for MATROX string. */
- if (size < 45 + sizeof(matrox))
- return;
- if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0)
- return;
-
- /* Get the PInS offset. */
- if (size < MGA_BIOS_OFFSET + 2)
- return;
- offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
-
- /* Get PInS data structure. */
-
- if (size < offset + 6)
- return;
- pins = bios + offset;
- if (pins[0] == 0x2e && pins[1] == 0x41) {
- version = pins[5];
- pins_len = pins[2];
- } else {
- version = 1;
- pins_len = pins[0] + (pins[1] << 8);
- }
-
- if (version < 1 || version > 5) {
- drm_warn(dev, "Unknown BIOS PInS version: %d\n", version);
- return;
- }
- if (pins_len != expected_length[version]) {
- drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n",
- pins_len, expected_length[version]);
- return;
- }
- if (size < offset + pins_len)
- return;
+ /* BAR 0 is VRAM */
- drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n",
- version, pins_len);
+ start = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
- /* Extract the clock values */
-
- switch (version) {
- case 1:
- tmp = pins[24] + (pins[25] << 8);
- if (tmp)
- mdev->model.g200.pclk_max = tmp * 10;
- break;
- case 2:
- if (pins[41] != 0xff)
- mdev->model.g200.pclk_max = (pins[41] + 100) * 1000;
- break;
- case 3:
- if (pins[36] != 0xff)
- mdev->model.g200.pclk_max = (pins[36] + 100) * 1000;
- if (pins[52] & 0x20)
- mdev->model.g200.ref_clk = 14318;
- break;
- case 4:
- if (pins[39] != 0xff)
- mdev->model.g200.pclk_max = pins[39] * 4 * 1000;
- if (pins[92] & 0x01)
- mdev->model.g200.ref_clk = 14318;
- break;
- case 5:
- tmp = pins[4] ? 8000 : 6000;
- if (pins[123] != 0xff)
- mdev->model.g200.pclk_min = pins[123] * tmp;
- if (pins[38] != 0xff)
- mdev->model.g200.pclk_max = pins[38] * tmp;
- if (pins[110] & 0x01)
- mdev->model.g200.ref_clk = 14318;
- break;
- default:
- break;
+ res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram");
+ if (!res) {
+ drm_err(dev, "devm_request_mem_region(VRAM) failed\n");
+ return -ENXIO;
}
-}
-
-static void mgag200_g200_init_refclk(struct mga_device *mdev)
-{
- struct drm_device *dev = &mdev->base;
- struct pci_dev *pdev = to_pci_dev(dev->dev);
- unsigned char __iomem *rom;
- unsigned char *bios;
- size_t size;
-
- mdev->model.g200.pclk_min = 50000;
- mdev->model.g200.pclk_max = 230000;
- mdev->model.g200.ref_clk = 27050;
+ mdev->vram_res = res;
- rom = pci_map_rom(pdev, &size);
- if (!rom)
- return;
+ /* Don't fail on errors, but performance might be reduced. */
+ devm_arch_io_reserve_memtype_wc(dev->dev, res->start, resource_size(res));
+ devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res));
- bios = vmalloc(size);
- if (!bios)
- goto out;
- memcpy_fromio(bios, rom, size);
-
- if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
- mgag200_g200_interpret_bios(mdev, bios, size);
-
- drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
- mdev->model.g200.pclk_min, mdev->model.g200.pclk_max,
- mdev->model.g200.ref_clk);
+ mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res));
+ if (!mdev->vram)
+ return -ENOMEM;
- vfree(bios);
-out:
- pci_unmap_rom(pdev, rom);
+ return 0;
}
-static void mgag200_g200se_init_unique_id(struct mga_device *mdev)
+int mgag200_device_init(struct mga_device *mdev, enum mga_type type,
+ const struct mgag200_device_info *info)
{
struct drm_device *dev = &mdev->base;
-
- /* stash G200 SE model number for later use */
- mdev->model.g200se.unique_rev_id = RREG32(0x1e24);
-
- drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
- mdev->model.g200se.unique_rev_id);
-}
-
-static struct mga_device *
-mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long flags)
-{
- struct mga_device *mdev;
- struct drm_device *dev;
+ u8 crtcext3, misc;
int ret;
- mdev = devm_drm_dev_alloc(&pdev->dev, &mgag200_driver, struct mga_device, base);
- if (IS_ERR(mdev))
- return mdev;
- dev = &mdev->base;
-
- pci_set_drvdata(pdev, dev);
-
- mdev->flags = flags;
+ mdev->info = info;
mdev->type = type;
- ret = mgag200_regs_init(mdev);
+ ret = drmm_mutex_init(dev, &mdev->rmmio_lock);
if (ret)
- return ERR_PTR(ret);
+ return ret;
- if (mdev->type == G200_PCI || mdev->type == G200_AGP)
- mgag200_g200_init_refclk(mdev);
- else if (IS_G200_SE(mdev))
- mgag200_g200se_init_unique_id(mdev);
+ mutex_lock(&mdev->rmmio_lock);
- ret = mgag200_mm_init(mdev);
- if (ret)
- return ERR_PTR(ret);
+ RREG_ECRT(0x03, crtcext3);
+ crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
+ WREG_ECRT(0x03, crtcext3);
- ret = mgag200_modeset_init(mdev);
- if (ret)
- return ERR_PTR(ret);
+ WREG_ECRT(0x04, 0x00);
- return mdev;
+ misc = RREG8(MGA_MISC_IN);
+ misc |= MGAREG_MISC_RAMMAPEN |
+ MGAREG_MISC_HIGH_PG_SEL;
+ WREG8(MGA_MISC_OUT, misc);
+
+ mutex_unlock(&mdev->rmmio_lock);
+
+ return 0;
}
/*
@@ -312,8 +201,7 @@ mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long fl
static const struct pci_device_id mgag200_pciidlist[] = {
{ PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
{ PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },
- { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD},
+ { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
{ PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
{ PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },
@@ -326,22 +214,10 @@ static const struct pci_device_id mgag200_pciidlist[] = {
MODULE_DEVICE_TABLE(pci, mgag200_pciidlist);
-static enum mga_type mgag200_type_from_driver_data(kernel_ulong_t driver_data)
-{
- return (enum mga_type)(driver_data & MGAG200_TYPE_MASK);
-}
-
-static unsigned long mgag200_flags_from_driver_data(kernel_ulong_t driver_data)
-{
- return driver_data & MGAG200_FLAG_MASK;
-}
-
static int
mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- kernel_ulong_t driver_data = ent->driver_data;
- enum mga_type type = mgag200_type_from_driver_data(driver_data);
- unsigned long flags = mgag200_flags_from_driver_data(driver_data);
+ enum mga_type type = (enum mga_type)ent->driver_data;
struct mga_device *mdev;
struct drm_device *dev;
int ret;
@@ -354,7 +230,37 @@ mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
- mdev = mgag200_device_create(pdev, type, flags);
+ switch (type) {
+ case G200_PCI:
+ case G200_AGP:
+ mdev = mgag200_g200_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_SE_A:
+ case G200_SE_B:
+ mdev = mgag200_g200se_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_WB:
+ mdev = mgag200_g200wb_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_EV:
+ mdev = mgag200_g200ev_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_EH:
+ mdev = mgag200_g200eh_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_EH3:
+ mdev = mgag200_g200eh3_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_ER:
+ mdev = mgag200_g200er_device_create(pdev, &mgag200_driver, type);
+ break;
+ case G200_EW3:
+ mdev = mgag200_g200ew3_device_create(pdev, &mgag200_driver, type);
+ break;
+ default:
+ dev_err(&pdev->dev, "Device type %d is unsupported\n", type);
+ return -ENODEV;
+ }
if (IS_ERR(mdev))
return PTR_ERR(mdev);
dev = &mdev->base;
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index a18384c41fc4..301c4ab46539 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -168,8 +168,6 @@ static inline struct mgag200_crtc_state *to_mgag200_crtc_state(struct drm_crtc_s
return container_of(base, struct mgag200_crtc_state, base);
}
-#define to_mga_connector(x) container_of(x, struct mga_connector, base)
-
struct mga_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
@@ -177,17 +175,6 @@ struct mga_i2c_chan {
int data, clock;
};
-struct mga_connector {
- struct drm_connector base;
- struct mga_i2c_chan *i2c;
-};
-
-struct mga_mc {
- resource_size_t vram_size;
- resource_size_t vram_base;
- resource_size_t vram_window;
-};
-
enum mga_type {
G200_PCI,
G200_AGP,
@@ -201,44 +188,66 @@ enum mga_type {
G200_EW3,
};
-/* HW does not handle 'startadd' field correct. */
-#define MGAG200_FLAG_HW_BUG_NO_STARTADD (1ul << 8)
+#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B)
-#define MGAG200_TYPE_MASK (0x000000ff)
-#define MGAG200_FLAG_MASK (0x00ffff00)
+struct mgag200_device_info {
+ u16 max_hdisplay;
+ u16 max_vdisplay;
+
+ /*
+ * Maximum memory bandwidth (MiB/sec). Setting this to zero disables
+ * the rsp test during mode validation.
+ */
+ unsigned long max_mem_bandwidth;
+
+ /* HW has external source (e.g., BMC) to synchronize with */
+ bool has_vidrst:1;
+
+ struct {
+ unsigned data_bit:3;
+ unsigned clock_bit:3;
+ } i2c;
+
+ /*
+ * HW does not handle 'startadd' register correctly. Always set
+ * it's value to 0.
+ */
+ bool bug_no_startadd:1;
+};
-#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B)
+#define MGAG200_DEVICE_INFO_INIT(_max_hdisplay, _max_vdisplay, _max_mem_bandwidth, \
+ _has_vidrst, _i2c_data_bit, _i2c_clock_bit, \
+ _bug_no_startadd) \
+ { \
+ .max_hdisplay = (_max_hdisplay), \
+ .max_vdisplay = (_max_vdisplay), \
+ .max_mem_bandwidth = (_max_mem_bandwidth), \
+ .has_vidrst = (_has_vidrst), \
+ .i2c = { \
+ .data_bit = (_i2c_data_bit), \
+ .clock_bit = (_i2c_clock_bit), \
+ }, \
+ .bug_no_startadd = (_bug_no_startadd), \
+ }
struct mga_device {
- struct drm_device base;
- unsigned long flags;
+ struct drm_device base;
- struct mutex rmmio_lock; /* Protects access to rmmio */
- resource_size_t rmmio_base;
- resource_size_t rmmio_size;
- void __iomem *rmmio;
+ const struct mgag200_device_info *info;
- struct mga_mc mc;
+ struct resource *rmmio_res;
+ void __iomem *rmmio;
+ struct mutex rmmio_lock; /* Protects access to rmmio */
+ struct resource *vram_res;
void __iomem *vram;
- size_t vram_fb_available;
+ resource_size_t vram_available;
enum mga_type type;
- union {
- struct {
- long ref_clk;
- long pclk_min;
- long pclk_max;
- } g200;
- struct {
- /* SE model number stored in reg 0x1e24 */
- u32 unique_rev_id;
- } g200se;
- } model;
-
- struct mga_connector connector;
struct mgag200_pll pixpll;
+ struct mga_i2c_chan i2c;
+ struct drm_connector connector;
struct drm_simple_display_pipe display_pipe;
};
@@ -247,15 +256,64 @@ static inline struct mga_device *to_mga_device(struct drm_device *dev)
return container_of(dev, struct mga_device, base);
}
+struct mgag200_g200_device {
+ struct mga_device base;
+
+ /* PLL constants */
+ long ref_clk;
+ long pclk_min;
+ long pclk_max;
+};
+
+static inline struct mgag200_g200_device *to_mgag200_g200_device(struct drm_device *dev)
+{
+ return container_of(to_mga_device(dev), struct mgag200_g200_device, base);
+}
+
+struct mgag200_g200se_device {
+ struct mga_device base;
+
+ /* SE model number stored in reg 0x1e24 */
+ u32 unique_rev_id;
+};
+
+static inline struct mgag200_g200se_device *to_mgag200_g200se_device(struct drm_device *dev)
+{
+ return container_of(to_mga_device(dev), struct mgag200_g200se_device, base);
+}
+
+ /* mgag200_drv.c */
+int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2);
+resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size);
+resource_size_t mgag200_device_probe_vram(struct mga_device *mdev);
+int mgag200_device_preinit(struct mga_device *mdev);
+int mgag200_device_init(struct mga_device *mdev, enum mga_type type,
+ const struct mgag200_device_info *info);
+
+ /* mgag200_<device type>.c */
+struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type);
+
/* mgag200_mode.c */
-int mgag200_modeset_init(struct mga_device *mdev);
+resource_size_t mgag200_device_probe_vram(struct mga_device *mdev);
+int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_fb_available);
/* mgag200_i2c.c */
-struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev);
-void mgag200_i2c_destroy(struct mga_i2c_chan *i2c);
-
- /* mgag200_mm.c */
-int mgag200_mm_init(struct mga_device *mdev);
+int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c);
/* mgag200_pll.c */
int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev);
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200.c b/drivers/gpu/drm/mgag200/mgag200_g200.c
new file mode 100644
index 000000000000..616e11391e02
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+static int mgag200_g200_init_pci_options(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ bool has_sgram;
+ u32 option;
+ int err;
+
+ err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
+ if (err != PCIBIOS_SUCCESSFUL) {
+ dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
+ return pcibios_err_to_errno(err);
+ }
+
+ has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK);
+
+ if (has_sgram)
+ option = 0x4049cd21;
+ else
+ option = 0x40499121;
+
+ return mgag200_init_pci_options(pdev, option, 0x00008000);
+}
+
+/*
+ * DRM Device
+ */
+
+static const struct mgag200_device_info mgag200_g200_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 3, false);
+
+static void mgag200_g200_interpret_bios(struct mgag200_g200_device *g200,
+ const unsigned char *bios, size_t size)
+{
+ static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'};
+ static const unsigned int expected_length[6] = {
+ 0, 64, 64, 64, 128, 128
+ };
+ struct mga_device *mdev = &g200->base;
+ struct drm_device *dev = &mdev->base;
+ const unsigned char *pins;
+ unsigned int pins_len, version;
+ int offset;
+ int tmp;
+
+ /* Test for MATROX string. */
+ if (size < 45 + sizeof(matrox))
+ return;
+ if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0)
+ return;
+
+ /* Get the PInS offset. */
+ if (size < MGA_BIOS_OFFSET + 2)
+ return;
+ offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
+
+ /* Get PInS data structure. */
+
+ if (size < offset + 6)
+ return;
+ pins = bios + offset;
+ if (pins[0] == 0x2e && pins[1] == 0x41) {
+ version = pins[5];
+ pins_len = pins[2];
+ } else {
+ version = 1;
+ pins_len = pins[0] + (pins[1] << 8);
+ }
+
+ if (version < 1 || version > 5) {
+ drm_warn(dev, "Unknown BIOS PInS version: %d\n", version);
+ return;
+ }
+ if (pins_len != expected_length[version]) {
+ drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n",
+ pins_len, expected_length[version]);
+ return;
+ }
+ if (size < offset + pins_len)
+ return;
+
+ drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n", version, pins_len);
+
+ /* Extract the clock values */
+
+ switch (version) {
+ case 1:
+ tmp = pins[24] + (pins[25] << 8);
+ if (tmp)
+ g200->pclk_max = tmp * 10;
+ break;
+ case 2:
+ if (pins[41] != 0xff)
+ g200->pclk_max = (pins[41] + 100) * 1000;
+ break;
+ case 3:
+ if (pins[36] != 0xff)
+ g200->pclk_max = (pins[36] + 100) * 1000;
+ if (pins[52] & 0x20)
+ g200->ref_clk = 14318;
+ break;
+ case 4:
+ if (pins[39] != 0xff)
+ g200->pclk_max = pins[39] * 4 * 1000;
+ if (pins[92] & 0x01)
+ g200->ref_clk = 14318;
+ break;
+ case 5:
+ tmp = pins[4] ? 8000 : 6000;
+ if (pins[123] != 0xff)
+ g200->pclk_min = pins[123] * tmp;
+ if (pins[38] != 0xff)
+ g200->pclk_max = pins[38] * tmp;
+ if (pins[110] & 0x01)
+ g200->ref_clk = 14318;
+ break;
+ default:
+ break;
+ }
+}
+
+static void mgag200_g200_init_refclk(struct mgag200_g200_device *g200)
+{
+ struct mga_device *mdev = &g200->base;
+ struct drm_device *dev = &mdev->base;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+ unsigned char __iomem *rom;
+ unsigned char *bios;
+ size_t size;
+
+ g200->pclk_min = 50000;
+ g200->pclk_max = 230000;
+ g200->ref_clk = 27050;
+
+ rom = pci_map_rom(pdev, &size);
+ if (!rom)
+ return;
+
+ bios = vmalloc(size);
+ if (!bios)
+ goto out;
+ memcpy_fromio(bios, rom, size);
+
+ if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
+ mgag200_g200_interpret_bios(g200, bios, size);
+
+ drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
+ g200->pclk_min, g200->pclk_max, g200->ref_clk);
+
+ vfree(bios);
+out:
+ pci_unmap_rom(pdev, rom);
+}
+
+struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mgag200_g200_device *g200;
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ g200 = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200_device, base.base);
+ if (IS_ERR(g200))
+ return ERR_CAST(g200);
+ mdev = &g200->base;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_g200_init_pci_options(pdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ mgag200_g200_init_refclk(g200);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh.c b/drivers/gpu/drm/mgag200/mgag200_g200eh.c
new file mode 100644
index 000000000000..1b9a22728744
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200eh.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200eh_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false);
+
+struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200eh_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c
new file mode 100644
index 000000000000..438cda1b14c9
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200eh3_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false);
+
+struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200eh3_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c
new file mode 100644
index 000000000000..0790d4e6463d
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200er_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false);
+
+struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200er_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
new file mode 100644
index 000000000000..5353422d0eef
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200ev_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 32700, false, 0, 1, false);
+
+struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200ev_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c
new file mode 100644
index 000000000000..3bfc1324cf78
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200ew3_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false);
+
+static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev)
+{
+ resource_size_t vram_size = resource_size(mdev->vram_res);
+
+ if (vram_size >= 0x1000000)
+ vram_size = vram_size - 0x400000;
+ return mgag200_probe_vram(mdev->vram, vram_size);
+}
+
+struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200ew3_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_g200ew3_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c
new file mode 100644
index 000000000000..0a3e66695e22
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+static int mgag200_g200se_init_pci_options(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ bool has_sgram;
+ u32 option;
+ int err;
+
+ err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
+ if (err != PCIBIOS_SUCCESSFUL) {
+ dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
+ return pcibios_err_to_errno(err);
+ }
+
+ has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK);
+
+ option = 0x40049120;
+ if (has_sgram)
+ option |= PCI_MGA_OPTION_HARDPWMSK;
+
+ return mgag200_init_pci_options(pdev, option, 0x00008000);
+}
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200se_a_01_device_info =
+ MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, true);
+
+static const struct mgag200_device_info mgag200_g200se_a_02_device_info =
+ MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, true);
+
+static const struct mgag200_device_info mgag200_g200se_a_03_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
+
+static const struct mgag200_device_info mgag200_g200se_b_01_device_info =
+ MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, false);
+
+static const struct mgag200_device_info mgag200_g200se_b_02_device_info =
+ MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, false);
+
+static const struct mgag200_device_info mgag200_g200se_b_03_device_info =
+ MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
+
+static int mgag200_g200se_init_unique_rev_id(struct mgag200_g200se_device *g200se)
+{
+ struct mga_device *mdev = &g200se->base;
+ struct drm_device *dev = &mdev->base;
+
+ /* stash G200 SE model number for later use */
+ g200se->unique_rev_id = RREG32(0x1e24);
+ if (!g200se->unique_rev_id)
+ return -ENODEV;
+
+ drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", g200se->unique_rev_id);
+
+ return 0;
+}
+
+struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mgag200_g200se_device *g200se;
+ const struct mgag200_device_info *info;
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ g200se = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200se_device, base.base);
+ if (IS_ERR(g200se))
+ return ERR_CAST(g200se);
+ mdev = &g200se->base;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_g200se_init_pci_options(pdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_g200se_init_unique_rev_id(g200se);
+ if (ret)
+ return ERR_PTR(ret);
+
+ switch (type) {
+ case G200_SE_A:
+ if (g200se->unique_rev_id >= 0x03)
+ info = &mgag200_g200se_a_03_device_info;
+ else if (g200se->unique_rev_id >= 0x02)
+ info = &mgag200_g200se_a_02_device_info;
+ else
+ info = &mgag200_g200se_a_01_device_info;
+ break;
+ case G200_SE_B:
+ if (g200se->unique_rev_id >= 0x03)
+ info = &mgag200_g200se_b_03_device_info;
+ else if (g200se->unique_rev_id >= 0x02)
+ info = &mgag200_g200se_b_02_device_info;
+ else
+ info = &mgag200_g200se_b_01_device_info;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = mgag200_device_init(mdev, type, info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200wb.c b/drivers/gpu/drm/mgag200/mgag200_g200wb.c
new file mode 100644
index 000000000000..c8450ac8eaec
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_g200wb.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "mgag200_drv.h"
+
+/*
+ * DRM device
+ */
+
+static const struct mgag200_device_info mgag200_g200wb_device_info =
+ MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false);
+
+struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
+ enum mga_type type)
+{
+ struct mga_device *mdev;
+ struct drm_device *dev;
+ resource_size_t vram_available;
+ int ret;
+
+ mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
+ if (IS_ERR(mdev))
+ return mdev;
+ dev = &mdev->base;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_preinit(mdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = mgag200_device_init(mdev, type, &mgag200_g200wb_device_info);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vram_available = mgag200_device_probe_vram(mdev);
+
+ ret = mgag200_modeset_init(mdev, vram_available);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return mdev;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c
index ac8e34eef513..0c48bdf3e7f8 100644
--- a/drivers/gpu/drm/mgag200/mgag200_i2c.c
+++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c
@@ -86,44 +86,25 @@ static int mga_gpio_getscl(void *data)
return (mga_i2c_read_gpio(mdev) & i2c->clock) ? 1 : 0;
}
-struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev)
+static void mgag200_i2c_release(void *res)
{
- struct mga_device *mdev = to_mga_device(dev);
- struct mga_i2c_chan *i2c;
+ struct mga_i2c_chan *i2c = res;
+
+ i2c_del_adapter(&i2c->adapter);
+}
+
+int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c)
+{
+ struct drm_device *dev = &mdev->base;
+ const struct mgag200_device_info *info = mdev->info;
int ret;
- int data, clock;
WREG_DAC(MGA1064_GEN_IO_CTL2, 1);
WREG_DAC(MGA1064_GEN_IO_DATA, 0xff);
WREG_DAC(MGA1064_GEN_IO_CTL, 0);
- switch (mdev->type) {
- case G200_SE_A:
- case G200_SE_B:
- case G200_EV:
- case G200_WB:
- case G200_EW3:
- data = 1;
- clock = 2;
- break;
- case G200_EH:
- case G200_EH3:
- case G200_ER:
- data = 2;
- clock = 1;
- break;
- default:
- data = 2;
- clock = 8;
- break;
- }
-
- i2c = kzalloc(sizeof(struct mga_i2c_chan), GFP_KERNEL);
- if (!i2c)
- return NULL;
-
- i2c->data = data;
- i2c->clock = clock;
+ i2c->data = BIT(info->i2c.data_bit);
+ i2c->clock = BIT(info->i2c.clock_bit);
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.class = I2C_CLASS_DDC;
i2c->adapter.dev.parent = dev->dev;
@@ -142,18 +123,8 @@ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev)
i2c->bit.getscl = mga_gpio_getscl;
ret = i2c_bit_add_bus(&i2c->adapter);
- if (ret) {
- kfree(i2c);
- i2c = NULL;
- }
- return i2c;
-}
+ if (ret)
+ return ret;
-void mgag200_i2c_destroy(struct mga_i2c_chan *i2c)
-{
- if (!i2c)
- return;
- i2c_del_adapter(&i2c->adapter);
- kfree(i2c);
+ return devm_add_action_or_reset(dev->dev, mgag200_i2c_release, i2c);
}
-
diff --git a/drivers/gpu/drm/mgag200/mgag200_mm.c b/drivers/gpu/drm/mgag200/mgag200_mm.c
deleted file mode 100644
index fa996d46feed..000000000000
--- a/drivers/gpu/drm/mgag200/mgag200_mm.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2012 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- * USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- */
-/*
- * Authors: Dave Airlie <airlied@redhat.com>
- */
-
-#include <linux/pci.h>
-
-#include <drm/drm_managed.h>
-
-#include "mgag200_drv.h"
-
-static size_t mgag200_probe_vram(struct mga_device *mdev, void __iomem *mem,
- size_t size)
-{
- int offset;
- int orig;
- int test1, test2;
- int orig1, orig2;
- size_t vram_size;
-
- /* Probe */
- orig = ioread16(mem);
- iowrite16(0, mem);
-
- vram_size = size;
-
- if ((mdev->type == G200_EW3) && (vram_size >= 0x1000000))
- vram_size = vram_size - 0x400000;
-
- for (offset = 0x100000; offset < vram_size; offset += 0x4000) {
- orig1 = ioread8(mem + offset);
- orig2 = ioread8(mem + offset + 0x100);
-
- iowrite16(0xaa55, mem + offset);
- iowrite16(0xaa55, mem + offset + 0x100);
-
- test1 = ioread16(mem + offset);
- test2 = ioread16(mem);
-
- iowrite16(orig1, mem + offset);
- iowrite16(orig2, mem + offset + 0x100);
-
- if (test1 != 0xaa55)
- break;
-
- if (test2)
- break;
- }
-
- iowrite16(orig, mem);
-
- return offset - 65536;
-}
-
-int mgag200_mm_init(struct mga_device *mdev)
-{
- struct drm_device *dev = &mdev->base;
- struct pci_dev *pdev = to_pci_dev(dev->dev);
- u8 misc;
- resource_size_t start, len;
-
- WREG_ECRT(0x04, 0x00);
-
- misc = RREG8(MGA_MISC_IN);
- misc |= MGAREG_MISC_RAMMAPEN |
- MGAREG_MISC_HIGH_PG_SEL;
- WREG8(MGA_MISC_OUT, misc);
-
- /* BAR 0 is VRAM */
- start = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
-
- if (!devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram")) {
- drm_err(dev, "can't reserve VRAM\n");
- return -ENXIO;
- }
-
- /* Don't fail on errors, but performance might be reduced. */
- devm_arch_io_reserve_memtype_wc(dev->dev, start, len);
- devm_arch_phys_wc_add(dev->dev, start, len);
-
- mdev->vram = devm_ioremap(dev->dev, start, len);
- if (!mdev->vram)
- return -ENOMEM;
-
- mdev->mc.vram_size = mgag200_probe_vram(mdev, mdev->vram, len);
- mdev->mc.vram_base = start;
- mdev->mc.vram_window = len;
-
- mdev->vram_fb_available = mdev->mc.vram_size;
-
- return 0;
-}
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index abde7655477d..e339f50d6bc8 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -32,57 +32,78 @@
* This file contains setup code for the CRTC.
*/
-static void mga_crtc_load_lut(struct drm_crtc *crtc)
+static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
+ const struct drm_format_info *format)
{
- struct drm_device *dev = crtc->dev;
- struct mga_device *mdev = to_mga_device(dev);
- struct drm_framebuffer *fb;
- u16 *r_ptr, *g_ptr, *b_ptr;
int i;
- if (!crtc->enabled)
- return;
-
- if (!mdev->display_pipe.plane.state)
- return;
+ WREG8(DAC_INDEX + MGA1064_INDEX, 0);
- fb = mdev->display_pipe.plane.state->fb;
+ switch (format->format) {
+ case DRM_FORMAT_RGB565:
+ /* Use better interpolation, to take 32 values from 0 to 255 */
+ for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
+ }
+ /* Green has one more bit, so add padding with 0 for red and blue. */
+ for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
+ }
+ break;
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ for (i = 0; i < MGAG200_LUT_SIZE; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
+ }
+ break;
+ default:
+ drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
+ &format->format);
+ break;
+ }
+}
- r_ptr = crtc->gamma_store;
- g_ptr = r_ptr + crtc->gamma_size;
- b_ptr = g_ptr + crtc->gamma_size;
+static void mgag200_crtc_set_gamma(struct mga_device *mdev,
+ const struct drm_format_info *format,
+ struct drm_color_lut *lut)
+{
+ int i;
WREG8(DAC_INDEX + MGA1064_INDEX, 0);
- if (fb && fb->format->cpp[0] * 8 == 16) {
- int inc = (fb->format->depth == 15) ? 8 : 4;
- u8 r, b;
- for (i = 0; i < MGAG200_LUT_SIZE; i += inc) {
- if (fb->format->depth == 16) {
- if (i > (MGAG200_LUT_SIZE >> 1)) {
- r = b = 0;
- } else {
- r = *r_ptr++ >> 8;
- b = *b_ptr++ >> 8;
- r_ptr++;
- b_ptr++;
- }
- } else {
- r = *r_ptr++ >> 8;
- b = *b_ptr++ >> 8;
- }
- /* VGA registers */
- WREG8(DAC_INDEX + MGA1064_COL_PAL, r);
- WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8);
- WREG8(DAC_INDEX + MGA1064_COL_PAL, b);
+ switch (format->format) {
+ case DRM_FORMAT_RGB565:
+ /* Use better interpolation, to take 32 values from lut[0] to lut[255] */
+ for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8);
}
- return;
- }
- for (i = 0; i < MGAG200_LUT_SIZE; i++) {
- /* VGA registers */
- WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8);
- WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8);
- WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8);
+ /* Green has one more bit, so add padding with 0 for red and blue. */
+ for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
+ }
+ break;
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ for (i = 0; i < MGAG200_LUT_SIZE; i++) {
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8);
+ WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8);
+ }
+ break;
+ default:
+ drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
+ &format->format);
+ break;
}
}
@@ -223,6 +244,9 @@ static void mgag200_set_startadd(struct mga_device *mdev,
startadd = offset / 8;
+ if (startadd > 0)
+ drm_WARN_ON_ONCE(dev, mdev->info->bug_no_startadd);
+
/*
* Can't store addresses any higher than that, but we also
* don't have more than 16 MiB of memory, so it should be fine.
@@ -353,6 +377,7 @@ static void mgag200_init_regs(struct mga_device *mdev)
static void mgag200_set_mode_regs(struct mga_device *mdev,
const struct drm_display_mode *mode)
{
+ const struct mgag200_device_info *info = mdev->info;
unsigned int hdisplay, hsyncstart, hsyncend, htotal;
unsigned int vdisplay, vsyncstart, vsyncend, vtotal;
u8 misc, crtcext1, crtcext2, crtcext5;
@@ -387,9 +412,9 @@ static void mgag200_set_mode_regs(struct mga_device *mdev,
((hdisplay & 0x100) >> 7) |
((hsyncstart & 0x100) >> 6) |
(htotal & 0x40);
- if (mdev->type == G200_WB || mdev->type == G200_EW3)
- crtcext1 |= BIT(7) | /* vrsten */
- BIT(3); /* hrsten */
+ if (info->has_vidrst)
+ crtcext1 |= MGAREG_CRTCEXT1_VRSTEN |
+ MGAREG_CRTCEXT1_HRSTEN;
crtcext2 = ((vtotal & 0xc00) >> 10) |
((vdisplay & 0x400) >> 8) |
@@ -559,13 +584,13 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb)
{
- u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
+ struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base);
unsigned int hiprilvl;
u8 crtcext6;
- if (unique_rev_id >= 0x04) {
+ if (g200se->unique_rev_id >= 0x04) {
hiprilvl = 0;
- } else if (unique_rev_id >= 0x02) {
+ } else if (g200se->unique_rev_id >= 0x02) {
unsigned int bpp;
unsigned long mb;
@@ -590,7 +615,7 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
else
hiprilvl = 5;
- } else if (unique_rev_id >= 0x01) {
+ } else if (g200se->unique_rev_id >= 0x01) {
hiprilvl = 3;
} else {
hiprilvl = 4;
@@ -665,176 +690,34 @@ static void mgag200_disable_display(struct mga_device *mdev)
* Connector
*/
-static int mga_vga_get_modes(struct drm_connector *connector)
-{
- struct mga_connector *mga_connector = to_mga_connector(connector);
- struct edid *edid;
- int ret = 0;
-
- edid = drm_get_edid(connector, &mga_connector->i2c->adapter);
- if (edid) {
- drm_connector_update_edid_property(connector, edid);
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
- }
- return ret;
-}
-
-static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode,
- int bits_per_pixel)
-{
- uint32_t total_area, divisor;
- uint64_t active_area, pixels_per_second, bandwidth;
- uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
-
- divisor = 1024;
-
- if (!mode->htotal || !mode->vtotal || !mode->clock)
- return 0;
-
- active_area = mode->hdisplay * mode->vdisplay;
- total_area = mode->htotal * mode->vtotal;
-
- pixels_per_second = active_area * mode->clock * 1000;
- do_div(pixels_per_second, total_area);
-
- bandwidth = pixels_per_second * bytes_per_pixel * 100;
- do_div(bandwidth, divisor);
-
- return (uint32_t)(bandwidth);
-}
-
-#define MODE_BANDWIDTH MODE_BAD
-
-static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
+static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector)
{
- struct drm_device *dev = connector->dev;
- struct mga_device *mdev = to_mga_device(dev);
- int bpp = 32;
-
- if (IS_G200_SE(mdev)) {
- u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
-
- if (unique_rev_id == 0x01) {
- if (mode->hdisplay > 1600)
- return MODE_VIRTUAL_X;
- if (mode->vdisplay > 1200)
- return MODE_VIRTUAL_Y;
- if (mga_vga_calculate_mode_bandwidth(mode, bpp)
- > (24400 * 1024))
- return MODE_BANDWIDTH;
- } else if (unique_rev_id == 0x02) {
- if (mode->hdisplay > 1920)
- return MODE_VIRTUAL_X;
- if (mode->vdisplay > 1200)
- return MODE_VIRTUAL_Y;
- if (mga_vga_calculate_mode_bandwidth(mode, bpp)
- > (30100 * 1024))
- return MODE_BANDWIDTH;
- } else {
- if (mga_vga_calculate_mode_bandwidth(mode, bpp)
- > (55000 * 1024))
- return MODE_BANDWIDTH;
- }
- } else if (mdev->type == G200_WB) {
- if (mode->hdisplay > 1280)
- return MODE_VIRTUAL_X;
- if (mode->vdisplay > 1024)
- return MODE_VIRTUAL_Y;
- if (mga_vga_calculate_mode_bandwidth(mode, bpp) >
- (31877 * 1024))
- return MODE_BANDWIDTH;
- } else if (mdev->type == G200_EV &&
- (mga_vga_calculate_mode_bandwidth(mode, bpp)
- > (32700 * 1024))) {
- return MODE_BANDWIDTH;
- } else if (mdev->type == G200_EH &&
- (mga_vga_calculate_mode_bandwidth(mode, bpp)
- > (37500 * 1024))) {
- return MODE_BANDWIDTH;
- } else if (mdev->type == G200_ER &&
- (mga_vga_calculate_mode_bandwidth(mode,
- bpp) > (55000 * 1024))) {
- return MODE_BANDWIDTH;
- }
-
- if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
- (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
- return MODE_H_ILLEGAL;
- }
-
- if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
- mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
- mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
- mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
- return MODE_BAD;
- }
-
- /* Validate the mode input by the user */
- if (connector->cmdline_mode.specified) {
- if (connector->cmdline_mode.bpp_specified)
- bpp = connector->cmdline_mode.bpp;
- }
-
- if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->vram_fb_available) {
- if (connector->cmdline_mode.specified)
- connector->cmdline_mode.specified = false;
- return MODE_BAD;
- }
+ struct mga_device *mdev = to_mga_device(connector->dev);
+ int ret;
- return MODE_OK;
-}
+ /*
+ * Protect access to I/O registers from concurrent modesetting
+ * by acquiring the I/O-register lock.
+ */
+ mutex_lock(&mdev->rmmio_lock);
+ ret = drm_connector_helper_get_modes_from_ddc(connector);
+ mutex_unlock(&mdev->rmmio_lock);
-static void mga_connector_destroy(struct drm_connector *connector)
-{
- struct mga_connector *mga_connector = to_mga_connector(connector);
- mgag200_i2c_destroy(mga_connector->i2c);
- drm_connector_cleanup(connector);
+ return ret;
}
static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
- .get_modes = mga_vga_get_modes,
- .mode_valid = mga_vga_mode_valid,
+ .get_modes = mgag200_vga_connector_helper_get_modes,
};
static const struct drm_connector_funcs mga_vga_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = mga_connector_destroy,
+ .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
-static int mgag200_vga_connector_init(struct mga_device *mdev)
-{
- struct drm_device *dev = &mdev->base;
- struct mga_connector *mconnector = &mdev->connector;
- struct drm_connector *connector = &mconnector->base;
- struct mga_i2c_chan *i2c;
- int ret;
-
- i2c = mgag200_i2c_create(dev);
- if (!i2c)
- drm_warn(dev, "failed to add DDC bus\n");
-
- ret = drm_connector_init_with_ddc(dev, connector,
- &mga_vga_connector_funcs,
- DRM_MODE_CONNECTOR_VGA,
- &i2c->adapter);
- if (ret)
- goto err_mgag200_i2c_destroy;
- drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
-
- mconnector->i2c = i2c;
-
- return 0;
-
-err_mgag200_i2c_destroy:
- mgag200_i2c_destroy(i2c);
- return ret;
-}
-
/*
* Simple Display Pipe
*/
@@ -843,6 +726,30 @@ static enum drm_mode_status
mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
const struct drm_display_mode *mode)
{
+ struct mga_device *mdev = to_mga_device(pipe->crtc.dev);
+ const struct mgag200_device_info *info = mdev->info;
+
+ /*
+ * Some devices have additional limits on the size of the
+ * display mode.
+ */
+ if (mode->hdisplay > info->max_hdisplay)
+ return MODE_VIRTUAL_X;
+ if (mode->vdisplay > info->max_vdisplay)
+ return MODE_VIRTUAL_Y;
+
+ if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
+ (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
+ return MODE_H_ILLEGAL;
+ }
+
+ if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
+ mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
+ mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
+ mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
+ return MODE_BAD;
+ }
+
return MODE_OK;
}
@@ -855,10 +762,6 @@ mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb,
dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip);
drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip);
-
- /* Always scanout image at VRAM offset 0 */
- mgag200_set_startadd(mdev, (u32)0);
- mgag200_set_offset(mdev, fb);
}
static void
@@ -908,11 +811,19 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
if (mdev->type == G200_WB || mdev->type == G200_EW3)
mgag200_g200wb_release_bmc(mdev);
- mga_crtc_load_lut(crtc);
+ if (crtc_state->gamma_lut)
+ mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data);
+ else
+ mgag200_crtc_set_gamma_linear(mdev, fb->format);
+
mgag200_enable_display(mdev);
mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]);
+ /* Always scanout image at VRAM offset 0 */
+ mgag200_set_startadd(mdev, (u32)0);
+ mgag200_set_offset(mdev, fb);
+
mutex_unlock(&mdev->rmmio_lock);
}
@@ -955,6 +866,14 @@ mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe,
return ret;
}
+ if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) {
+ if (crtc_state->gamma_lut->length !=
+ MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) {
+ drm_err(dev, "Wrong size for gamma_lut %zu\n",
+ crtc_state->gamma_lut->length);
+ return -EINVAL;
+ }
+ }
return 0;
}
@@ -963,20 +882,30 @@ mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane *plane = &pipe->plane;
+ struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *dev = plane->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_plane_state *state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_framebuffer *fb = state->fb;
struct drm_rect damage;
+ struct drm_atomic_helper_damage_iter iter;
if (!fb)
return;
mutex_lock(&mdev->rmmio_lock);
- if (drm_atomic_helper_damage_merged(old_state, state, &damage))
+ if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut)
+ mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data);
+
+ drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_for_each_plane_damage(&iter, &damage) {
mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]);
+ }
+ /* Always scanout image at VRAM offset 0 */
+ mgag200_set_startadd(mdev, (u32)0);
+ mgag200_set_offset(mdev, fb);
mutex_unlock(&mdev->rmmio_lock);
}
@@ -1056,30 +985,81 @@ static const uint64_t mgag200_simple_display_pipe_fmtmods[] = {
* Mode config
*/
-static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
- .fb_create = drm_gem_fb_create_with_dirty,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
-};
+/* Calculates a mode's required memory bandwidth (in KiB/sec). */
+static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode,
+ unsigned int bits_per_pixel)
+{
+ uint32_t total_area, divisor;
+ uint64_t active_area, pixels_per_second, bandwidth;
+ uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
+
+ divisor = 1024;
-static unsigned int mgag200_preferred_depth(struct mga_device *mdev)
+ if (!mode->htotal || !mode->vtotal || !mode->clock)
+ return 0;
+
+ active_area = mode->hdisplay * mode->vdisplay;
+ total_area = mode->htotal * mode->vtotal;
+
+ pixels_per_second = active_area * mode->clock * 1000;
+ do_div(pixels_per_second, total_area);
+
+ bandwidth = pixels_per_second * bytes_per_pixel * 100;
+ do_div(bandwidth, divisor);
+
+ return (uint32_t)bandwidth;
+}
+
+static enum drm_mode_status mgag200_mode_config_mode_valid(struct drm_device *dev,
+ const struct drm_display_mode *mode)
{
- if (IS_G200_SE(mdev) && mdev->vram_fb_available < (2048*1024))
- return 16;
- else
- return 32;
+ static const unsigned int max_bpp = 4; // DRM_FORMAT_XRGB8888
+ struct mga_device *mdev = to_mga_device(dev);
+ unsigned long fbsize, fbpages, max_fbpages;
+ const struct mgag200_device_info *info = mdev->info;
+
+ max_fbpages = mdev->vram_available >> PAGE_SHIFT;
+
+ fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
+ fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
+
+ if (fbpages > max_fbpages)
+ return MODE_MEM;
+
+ /*
+ * Test the mode's required memory bandwidth if the device
+ * specifies a maximum. Not all devices do though.
+ */
+ if (info->max_mem_bandwidth) {
+ uint32_t mode_bandwidth = mgag200_calculate_mode_bandwidth(mode, max_bpp * 8);
+
+ if (mode_bandwidth > (info->max_mem_bandwidth * 1024))
+ return MODE_BAD;
+ }
+
+ return MODE_OK;
}
-int mgag200_modeset_init(struct mga_device *mdev)
+static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create_with_dirty,
+ .mode_valid = mgag200_mode_config_mode_valid,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_available)
{
struct drm_device *dev = &mdev->base;
- struct drm_connector *connector = &mdev->connector.base;
+ struct mga_i2c_chan *i2c = &mdev->i2c;
+ struct drm_connector *connector = &mdev->connector;
struct drm_simple_display_pipe *pipe = &mdev->display_pipe;
size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats);
int ret;
mgag200_init_regs(mdev);
+ mdev->vram_available = vram_available;
+
ret = drmm_mode_config_init(dev);
if (ret) {
drm_err(dev, "drmm_mode_config_init() failed, error %d\n",
@@ -1089,21 +1069,26 @@ int mgag200_modeset_init(struct mga_device *mdev)
dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
-
- dev->mode_config.preferred_depth = mgag200_preferred_depth(mdev);
-
- dev->mode_config.fb_base = mdev->mc.vram_base;
-
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.fb_base = mdev->vram_res->start;
dev->mode_config.funcs = &mgag200_mode_config_funcs;
- ret = mgag200_vga_connector_init(mdev);
+ ret = mgag200_i2c_init(mdev, i2c);
if (ret) {
- drm_err(dev,
- "mgag200_vga_connector_init() failed, error %d\n",
- ret);
+ drm_err(dev, "failed to add DDC bus: %d\n", ret);
return ret;
}
+ ret = drm_connector_init_with_ddc(dev, connector,
+ &mga_vga_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA,
+ &i2c->adapter);
+ if (ret) {
+ drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret);
+ return ret;
+ }
+ drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
+
ret = mgag200_pixpll_init(&mdev->pixpll, mdev);
if (ret)
return ret;
@@ -1121,9 +1106,13 @@ int mgag200_modeset_init(struct mga_device *mdev)
return ret;
}
- /* FIXME: legacy gamma tables; convert to CRTC state */
+ drm_plane_enable_fb_damage_clips(&pipe->plane);
+
+ /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE);
+ drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE);
+
drm_mode_config_reset(dev);
return 0;
diff --git a/drivers/gpu/drm/mgag200/mgag200_pll.c b/drivers/gpu/drm/mgag200/mgag200_pll.c
index 52be08b744ad..8065ca5d8de9 100644
--- a/drivers/gpu/drm/mgag200/mgag200_pll.c
+++ b/drivers/gpu/drm/mgag200/mgag200_pll.c
@@ -13,6 +13,7 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock,
{
struct mga_device *mdev = pixpll->mdev;
struct drm_device *dev = &mdev->base;
+ struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev);
const int post_div_max = 7;
const int in_div_min = 1;
const int in_div_max = 6;
@@ -23,9 +24,9 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock,
long f_vco;
long computed;
long delta, tmp_delta;
- long ref_clk = mdev->model.g200.ref_clk;
- long p_clk_min = mdev->model.g200.pclk_min;
- long p_clk_max = mdev->model.g200.pclk_max;
+ long ref_clk = g200->ref_clk;
+ long p_clk_min = g200->pclk_min;
+ long p_clk_max = g200->pclk_max;
if (clock > p_clk_max) {
drm_err(dev, "Pixel Clock %ld too high\n", clock);
@@ -951,6 +952,7 @@ static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200ew3 = {
int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
+ struct mgag200_g200se_device *g200se;
pixpll->mdev = mdev;
@@ -961,7 +963,9 @@ int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev)
break;
case G200_SE_A:
case G200_SE_B:
- if (mdev->model.g200se.unique_rev_id >= 0x04)
+ g200se = to_mgag200_g200se_device(dev);
+
+ if (g200se->unique_rev_id >= 0x04)
pixpll->funcs = &mgag200_pixpll_funcs_g200se_04;
else
pixpll->funcs = &mgag200_pixpll_funcs_g200se_00;
diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h
index 60e705283fe8..99a9ab7d9119 100644
--- a/drivers/gpu/drm/mgag200/mgag200_reg.h
+++ b/drivers/gpu/drm/mgag200/mgag200_reg.h
@@ -252,8 +252,10 @@
#define MGAREG_CRTCEXT0_OFFSET_MASK GENMASK(5, 4)
+#define MGAREG_CRTCEXT1_VRSTEN BIT(7)
#define MGAREG_CRTCEXT1_VSYNCOFF BIT(5)
#define MGAREG_CRTCEXT1_HSYNCOFF BIT(4)
+#define MGAREG_CRTCEXT1_HRSTEN BIT(3)
#define MGAREG_CRTCEXT3_MGAMODE BIT(7)
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 4f9b3aa5deda..5226323e55d3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -39,6 +39,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_atomic.h>
@@ -605,6 +606,7 @@ nouveau_fbcon_fini(struct drm_device *dev)
if (!drm->fbcon)
return;
+ drm_kms_helper_poll_fini(dev);
nouveau_fbcon_accel_fini(dev);
nouveau_fbcon_destroy(dev, drm->fbcon);
kfree(drm->fbcon);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
index 2ed528c065fa..ac9e122586bc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
@@ -41,11 +41,9 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
ret = clk_prepare_enable(tdev->clk);
if (ret)
goto err_clk;
- if (tdev->clk_ref) {
- ret = clk_prepare_enable(tdev->clk_ref);
- if (ret)
- goto err_clk_ref;
- }
+ ret = clk_prepare_enable(tdev->clk_ref);
+ if (ret)
+ goto err_clk_ref;
ret = clk_prepare_enable(tdev->clk_pwr);
if (ret)
goto err_clk_pwr;
@@ -70,8 +68,7 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
err_clamp:
clk_disable_unprepare(tdev->clk_pwr);
err_clk_pwr:
- if (tdev->clk_ref)
- clk_disable_unprepare(tdev->clk_ref);
+ clk_disable_unprepare(tdev->clk_ref);
err_clk_ref:
clk_disable_unprepare(tdev->clk);
err_clk:
@@ -87,8 +84,7 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev)
int ret;
clk_disable_unprepare(tdev->clk_pwr);
- if (tdev->clk_ref)
- clk_disable_unprepare(tdev->clk_ref);
+ clk_disable_unprepare(tdev->clk_ref);
clk_disable_unprepare(tdev->clk);
udelay(10);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c
index 70e16a91ac12..faf0fe9f704c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c
@@ -52,7 +52,7 @@ gv100_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *cgrp,
nvkm_wo32(memory, offset + 0xc, 0x00000000);
}
-const struct gk104_fifo_runlist_func
+static const struct gk104_fifo_runlist_func
gv100_fifo_runlist = {
.size = 16,
.cgrp = gv100_fifo_runlist_cgrp,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 397ff4fe9df8..f16eabf4f642 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -1922,8 +1922,8 @@ gf100_gr_oneinit_tiles(struct gf100_gr *gr)
for (i = 0; i < gr->gpc_nr; i++) {
init_frac[i] = gr->tpc_nr[gpc_map[i]] * gr->gpc_nr * mul_factor;
- init_err[i] = i * gr->tpc_max * mul_factor - comm_denom/2;
- run_err[i] = init_frac[i] + init_err[i];
+ init_err[i] = i * gr->tpc_max * mul_factor - comm_denom/2;
+ run_err[i] = init_frac[i] + init_err[i];
}
for (i = 0; i < gr->tpc_total;) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
index 64e423dddd9e..6c318e41bde0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
@@ -33,7 +33,7 @@ nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size)
{
u32 p = *addr;
- if (*addr > bios->image0_size && bios->imaged_addr) {
+ if (*addr >= bios->image0_size && bios->imaged_addr) {
*addr -= bios->image0_size;
*addr += bios->imaged_addr;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
index 8bf00b396ec1..ca74775834dd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
@@ -280,7 +280,7 @@ nvkm_vmm_unref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes)
if (desc->type == SPT && (pgt->refs[0] || pgt->refs[1]))
nvkm_vmm_unref_sptes(it, pgt, desc, ptei, ptes);
- /* PT no longer neeed? Destroy it. */
+ /* PT no longer needed? Destroy it. */
if (!pgt->refs[type]) {
it->lvl++;
TRA(it, "%s empty", nvkm_vmm_desc_type(desc));
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 38799effd00a..4f1f004b3c54 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -438,6 +438,8 @@ config DRM_PANEL_SAMSUNG_ATNA33XC20
depends on OF
depends on BACKLIGHT_CLASS_DEVICE
depends on PM
+ select DRM_DISPLAY_DP_HELPER
+ select DRM_DISPLAY_HELPER
select DRM_DP_AUX_BUS
help
DRM panel driver for the Samsung ATNA33XC20 panel. This panel can't
diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
index a9cd7135cb51..ee61d60eceae 100644
--- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
+++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
@@ -209,7 +209,7 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd),
"Couldn't get avdd regulator\n");
- ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
+ ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
"Couldn't get our reset GPIO\n");
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 7f51a4682ccb..ee612303f076 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -11,6 +11,7 @@
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
#include "panfrost_features.h"
+#include "panfrost_issues.h"
#include "panfrost_gpu.h"
#include "panfrost_job.h"
#include "panfrost_mmu.h"
@@ -380,9 +381,13 @@ const char *panfrost_exception_name(u32 exception_code)
bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
u32 exception_code)
{
- /* Right now, none of the GPU we support need a reset, but this
- * might change.
+ /* If an occlusion query write causes a bus fault on affected GPUs,
+ * future fragment jobs may hang. Reset to workaround.
*/
+ if (exception_code == DRM_PANFROST_EXCEPTION_JOB_BUS_FAULT)
+ return panfrost_has_hw_issue(pfdev, HW_ISSUE_TTRX_3076);
+
+ /* No other GPUs we support need a reset */
return false;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 087e69b98d06..c58075bc096e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -665,6 +665,7 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "arm,mali-t860", .data = &default_data, },
{ .compatible = "arm,mali-t880", .data = &default_data, },
{ .compatible = "arm,mali-bifrost", .data = &default_data, },
+ { .compatible = "arm,mali-valhall-jm", .data = &default_data, },
{ .compatible = "mediatek,mt8183-mali", .data = &mediatek_mt8183_data },
{}
};
diff --git a/drivers/gpu/drm/panfrost/panfrost_features.h b/drivers/gpu/drm/panfrost/panfrost_features.h
index 36fadcf9634e..7ed0cd3ea2d4 100644
--- a/drivers/gpu/drm/panfrost/panfrost_features.h
+++ b/drivers/gpu/drm/panfrost/panfrost_features.h
@@ -21,6 +21,7 @@ enum panfrost_hw_feature {
HW_FEATURE_TLS_HASHING,
HW_FEATURE_THREAD_GROUP_SPLIT,
HW_FEATURE_IDVS_GROUP_SIZE,
+ HW_FEATURE_CLEAN_ONLY_SAFE,
HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG,
};
@@ -105,6 +106,18 @@ enum panfrost_hw_feature {
BIT_ULL(HW_FEATURE_TLS_HASHING) | \
BIT_ULL(HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG))
+#define hw_features_g57 (\
+ BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
+ BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
+ BIT_ULL(HW_FEATURE_XAFFINITY) | \
+ BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
+ BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
+ BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \
+ BIT_ULL(HW_FEATURE_COHERENCY_REG) | \
+ BIT_ULL(HW_FEATURE_AARCH64_MMU) | \
+ BIT_ULL(HW_FEATURE_IDVS_GROUP_SIZE) | \
+ BIT_ULL(HW_FEATURE_CLEAN_ONLY_SAFE))
+
static inline bool panfrost_has_hw_feature(struct panfrost_device *pfdev,
enum panfrost_hw_feature feat)
{
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index aa89926742fd..6452e4e900dd 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -108,6 +108,9 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev)
quirks |= SC_LS_ALLOW_ATTR_TYPES;
}
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_TTRX_2968_TTRX_3162))
+ quirks |= SC_VAR_ALGORITHM;
+
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_TLS_HASHING))
quirks |= SC_TLS_HASH_ENABLE;
@@ -124,18 +127,6 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev)
gpu_write(pfdev, GPU_TILER_CONFIG, quirks);
- quirks = gpu_read(pfdev, GPU_L2_MMU_CONFIG);
-
- /* Limit read & write ID width for AXI */
- if (panfrost_has_hw_feature(pfdev, HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG))
- quirks &= ~(L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_READS |
- L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_WRITES);
- else
- quirks &= ~(L2_MMU_CONFIG_LIMIT_EXTERNAL_READS |
- L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES);
-
- gpu_write(pfdev, GPU_L2_MMU_CONFIG, quirks);
-
quirks = 0;
if ((panfrost_model_eq(pfdev, 0x860) || panfrost_model_eq(pfdev, 0x880)) &&
pfdev->features.revision >= 0x2000)
@@ -210,6 +201,9 @@ static const struct panfrost_model gpu_models[] = {
GPU_MODEL(g52, 0x7002),
GPU_MODEL(g31, 0x7003,
GPU_REV(g31, 1, 0)),
+
+ GPU_MODEL(g57, 0x9001,
+ GPU_REV(g57, 0, 0)),
};
static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_issues.h b/drivers/gpu/drm/panfrost/panfrost_issues.h
index 501a76c5e95f..eb60cb83667a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_issues.h
+++ b/drivers/gpu/drm/panfrost/panfrost_issues.h
@@ -125,6 +125,16 @@ enum panfrost_hw_issue {
* kernel must fiddle with L2 caches to prevent data leakage */
HW_ISSUE_TGOX_R1_1234,
+ /* Must set SC_VAR_ALGORITHM */
+ HW_ISSUE_TTRX_2968_TTRX_3162,
+
+ /* Bus fault from occlusion query write may cause future fragment jobs
+ * to hang */
+ HW_ISSUE_TTRX_3076,
+
+ /* Must issue a dummy job before starting real work to prevent hangs */
+ HW_ISSUE_TTRX_3485,
+
HW_ISSUE_END
};
@@ -248,7 +258,14 @@ enum panfrost_hw_issue {
#define hw_issues_g76 0
-static inline bool panfrost_has_hw_issue(struct panfrost_device *pfdev,
+#define hw_issues_g57 (\
+ BIT_ULL(HW_ISSUE_TTRX_2968_TTRX_3162) | \
+ BIT_ULL(HW_ISSUE_TTRX_3076))
+
+#define hw_issues_g57_r0p0 (\
+ BIT_ULL(HW_ISSUE_TTRX_3485))
+
+static inline bool panfrost_has_hw_issue(const struct panfrost_device *pfdev,
enum panfrost_hw_issue issue)
{
return test_bit(issue, pfdev->features.hw_issues);
diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h
index 0b6cd8fdcb47..accb4fa3adb8 100644
--- a/drivers/gpu/drm/panfrost/panfrost_regs.h
+++ b/drivers/gpu/drm/panfrost/panfrost_regs.h
@@ -195,6 +195,7 @@
#define SC_TLS_HASH_ENABLE BIT(17)
#define SC_LS_ATTR_CHECK_DISABLE BIT(18)
#define SC_ENABLE_TEXGRD_FLAGS BIT(25)
+#define SC_VAR_ALGORITHM BIT(29)
/* End SHADER_CONFIG register */
/* TILER_CONFIG register */
diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h
index 2e1bc01aa5c9..970e192b0d51 100644
--- a/drivers/gpu/drm/r128/r128_drv.h
+++ b/drivers/gpu/drm/r128/r128_drv.h
@@ -300,8 +300,8 @@ extern long r128_compat_ioctl(struct file *filp, unsigned int cmd,
# define R128_PM4_64PIO_128INDBM (5 << 28)
# define R128_PM4_64BM_128INDBM (6 << 28)
# define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28)
-# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28)
-# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28)
+# define R128_PM4_64BM_64VCBM_64INDBM (8U << 28)
+# define R128_PM4_64PIO_64VCPIO_64INDPIO (15U << 28)
# define R128_PM4_BUFFER_CNTL_NOUPDATE (1 << 27)
#define R128_PM4_BUFFER_WM_CNTL 0x0708
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 5afab49dc4f2..53c2d9980d48 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -2,7 +2,6 @@
config DRM_ROCKCHIP
tristate "DRM Support for Rockchip"
depends on DRM && ROCKCHIP_IOMMU
- select DRM_DISPLAY_HELPER if ROCKCHIP_ANALOGIX_DP
select DRM_GEM_CMA_HELPER
select DRM_KMS_HELPER
select DRM_PANEL
@@ -38,6 +37,7 @@ config ROCKCHIP_VOP2
config ROCKCHIP_ANALOGIX_DP
bool "Rockchip specific extensions for Analogix DP driver"
depends on ROCKCHIP_VOP
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DP_HELPER
help
This selects support for Rockchip SoC specific extensions
@@ -47,6 +47,8 @@ config ROCKCHIP_ANALOGIX_DP
config ROCKCHIP_CDN_DP
bool "Rockchip cdn DP"
depends on EXTCON=y || (EXTCON=m && DRM_ROCKCHIP=m)
+ select DRM_DISPLAY_HELPER
+ select DRM_DISPLAY_DP_HELPER
help
This selects support for Rockchip SoC specific extensions
for the cdn DP driver. If you want to enable Dp on
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 26ac91db0f35..6b72894e02d4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -1202,7 +1202,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
*/
stride = (fb->pitches[0] << 3) / bpp;
if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
- drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n",
+ drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
vp->id, win->data->name, stride);
rb_swap = vop2_afbc_rb_swap(fb->format->format);
@@ -1473,7 +1473,7 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
default:
drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
return;
- };
+ }
dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
@@ -1524,6 +1524,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
if (ret < 0) {
drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
vp->id, ret);
+ vop2_unlock(vop2);
return;
}
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index 191c56064f19..6b25b2f4f5a3 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -190,7 +190,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
}
EXPORT_SYMBOL(drm_sched_entity_flush);
-static void drm_sched_entity_kill_jobs_irq_work(struct irq_work *wrk)
+static void drm_sched_entity_kill_jobs_work(struct work_struct *wrk)
{
struct drm_sched_job *job = container_of(wrk, typeof(*job), work);
@@ -207,8 +207,8 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
finish_cb);
- init_irq_work(&job->work, drm_sched_entity_kill_jobs_irq_work);
- irq_work_queue(&job->work);
+ INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work);
+ schedule_work(&job->work);
}
static struct dma_fence *
diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c
index 43722adab1f8..07802907e39a 100644
--- a/drivers/gpu/drm/solomon/ssd130x-spi.c
+++ b/drivers/gpu/drm/solomon/ssd130x-spi.c
@@ -143,6 +143,7 @@ static const struct of_device_id ssd130x_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ssd130x_of_match);
+#if IS_MODULE(CONFIG_DRM_SSD130X_SPI)
/*
* The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
* if the device was registered via OF. This means that the module will not be
@@ -160,6 +161,7 @@ static const struct spi_device_id ssd130x_spi_table[] = {
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, ssd130x_spi_table);
+#endif
static struct spi_driver ssd130x_spi_driver = {
.driver = {
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c
index 29d618093e94..e0f02d367d88 100644
--- a/drivers/gpu/drm/tiny/st7735r.c
+++ b/drivers/gpu/drm/tiny/st7735r.c
@@ -174,6 +174,7 @@ MODULE_DEVICE_TABLE(of, st7735r_of_match);
static const struct spi_device_id st7735r_id[] = {
{ "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg },
+ { "rh128128t", (uintptr_t)&rh128128t_cfg },
{ },
};
MODULE_DEVICE_TABLE(spi, st7735r_id);
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 75d308ec173d..296af2b89951 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -44,12 +44,6 @@
#include "ttm_module.h"
-/* default destructor */
-static void ttm_bo_default_destroy(struct ttm_buffer_object *bo)
-{
- kfree(bo);
-}
-
static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
@@ -936,8 +930,7 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,
bool locked;
int ret;
- bo->destroy = destroy ? destroy : ttm_bo_default_destroy;
-
+ bo->destroy = destroy;
kref_init(&bo->kref);
INIT_LIST_HEAD(&bo->ddestroy);
bo->bdev = bdev;
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index 930574ad2bca..318fdb38b47a 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -128,7 +128,7 @@ struct drm_connector *udl_connector_init(struct drm_device *dev)
connector = &udl_connector->connector;
drm_connector_init(dev, connector, &udl_connector_funcs,
- DRM_MODE_CONNECTOR_DVII);
+ DRM_MODE_CONNECTOR_VGA);
drm_connector_helper_add(connector, &udl_connector_helper_funcs);
connector->polled = DRM_CONNECTOR_POLL_HPD |
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index c6a1036bf2ea..914c0ac7dc8b 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -213,7 +213,7 @@ static int compose_active_planes(void **vaddr_out,
*
* Work handler for composing and computing CRCs. work_struct scheduled in
* an ordered workqueue that's periodically scheduled to run by
- * _vblank_handle() and flushed at vkms_atomic_crtc_destroy_state().
+ * vkms_vblank_simulate() and flushed at vkms_atomic_commit_tail().
*/
void vkms_composer_worker(struct work_struct *work)
{
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
index eb94433067ba..85a66014c2b6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -393,6 +393,12 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo)
kfree(vmw_bo);
}
+/* default destructor */
+static void vmw_bo_default_destroy(struct ttm_buffer_object *bo)
+{
+ kfree(bo);
+}
+
/**
* vmw_bo_create_kernel - Create a pinned BO for internal kernel use.
*
@@ -425,7 +431,7 @@ int vmw_bo_create_kernel(struct vmw_private *dev_priv, unsigned long size,
ret = ttm_bo_init_reserved(&dev_priv->bdev, bo, size,
ttm_bo_type_kernel, placement, 0,
- &ctx, NULL, NULL, NULL);
+ &ctx, NULL, NULL, vmw_bo_default_destroy);
if (unlikely(ret))
goto error_free;
@@ -448,6 +454,8 @@ int vmw_bo_create(struct vmw_private *vmw,
{
int ret;
+ BUG_ON(!bo_free);
+
*p_bo = kmalloc(sizeof(**p_bo), GFP_KERNEL);
if (unlikely(!*p_bo)) {
DRM_ERROR("Failed to allocate a buffer.\n");
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index f2a6b81e45c4..bd849013f16f 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -616,7 +616,7 @@ config FB_UVESA
config FB_VESA
bool "VESA VGA graphics support"
- depends on (FB = y) && X86
+ depends on (FB = y) && (X86 || COMPILE_TEST)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index afa2863670f3..2a1a425ceb98 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1555,6 +1555,7 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
{
int i;
+restart_removal:
/* check all firmware fbs and kick off if the base addr overlaps */
for_each_registered_fb(i) {
struct apertures_struct *gen_aper;
@@ -1587,12 +1588,23 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
pr_warn("fb%d: no device set\n", i);
do_unregister_framebuffer(registered_fb[i]);
} else if (dev_is_platform(device)) {
- registered_fb[i]->forced_out = true;
+ /*
+ * Drop the lock because if the device is unregistered, its
+ * driver will call to unregister_framebuffer(), that takes
+ * this lock.
+ */
+ mutex_unlock(&registration_lock);
platform_device_unregister(to_platform_device(device));
+ mutex_lock(&registration_lock);
} else {
pr_warn("fb%d: cannot remove device\n", i);
do_unregister_framebuffer(registered_fb[i]);
}
+ /*
+ * Restart the removal loop now that the device has been
+ * unregistered and its associated framebuffer gone.
+ */
+ goto restart_removal;
}
}
}
@@ -1852,13 +1864,9 @@ EXPORT_SYMBOL(register_framebuffer);
void
unregister_framebuffer(struct fb_info *fb_info)
{
- bool forced_out = fb_info->forced_out;
-
- if (!forced_out)
- mutex_lock(&registration_lock);
+ mutex_lock(&registration_lock);
do_unregister_framebuffer(fb_info);
- if (!forced_out)
- mutex_unlock(&registration_lock);
+ mutex_unlock(&registration_lock);
}
EXPORT_SYMBOL(unregister_framebuffer);
diff --git a/include/drm/display/drm_dp_aux_bus.h b/include/drm/display/drm_dp_aux_bus.h
index 4f19b20b1dd6..8a0a486383c5 100644
--- a/include/drm/display/drm_dp_aux_bus.h
+++ b/include/drm/display/drm_dp_aux_bus.h
@@ -44,9 +44,37 @@ static inline struct dp_aux_ep_driver *to_dp_aux_ep_drv(struct device_driver *dr
return container_of(drv, struct dp_aux_ep_driver, driver);
}
-int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
-void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux);
-int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
+int of_dp_aux_populate_bus(struct drm_dp_aux *aux,
+ int (*done_probing)(struct drm_dp_aux *aux));
+void of_dp_aux_depopulate_bus(struct drm_dp_aux *aux);
+int devm_of_dp_aux_populate_bus(struct drm_dp_aux *aux,
+ int (*done_probing)(struct drm_dp_aux *aux));
+
+/* Deprecated versions of the above functions. To be removed when no callers. */
+static inline int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+{
+ int ret;
+
+ ret = of_dp_aux_populate_bus(aux, NULL);
+
+ /* New API returns -ENODEV for no child case; adapt to old assumption */
+ return (ret != -ENODEV) ? ret : 0;
+}
+
+static inline int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+{
+ int ret;
+
+ ret = devm_of_dp_aux_populate_bus(aux, NULL);
+
+ /* New API returns -ENODEV for no child case; adapt to old assumption */
+ return (ret != -ENODEV) ? ret : 0;
+}
+
+static inline void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux)
+{
+ of_dp_aux_depopulate_bus(aux);
+}
#define dp_aux_dp_driver_register(aux_ep_drv) \
__dp_aux_dp_driver_register(aux_ep_drv, THIS_MODULE)
diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h
index dca40a045dd6..dc3c02225fcf 100644
--- a/include/drm/display/drm_dp_helper.h
+++ b/include/drm/display/drm_dp_helper.h
@@ -370,9 +370,19 @@ struct drm_dp_aux {
* helpers assume this is the case.
*
* Also note that this callback can be called no matter the
- * state @dev is in. Drivers that need that device to be powered
- * to perform this operation will first need to make sure it's
- * been properly enabled.
+ * state @dev is in and also no matter what state the panel is
+ * in. It's expected:
+ * - If the @dev providing the AUX bus is currently unpowered then
+ * it will power itself up for the transfer.
+ * - If we're on eDP (using a drm_panel) and the panel is not in a
+ * state where it can respond (it's not powered or it's in a
+ * low power state) then this function may return an error, but
+ * not crash. It's up to the caller of this code to make sure that
+ * the panel is powered on if getting an error back is not OK. If a
+ * drm_panel driver is initiating a DP AUX transfer it may power
+ * itself up however it wants. All other code should ensure that
+ * the pre_enable() bridge chain (which eventually calls the
+ * drm_panel prepare function) has powered the panel.
*/
ssize_t (*transfer)(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index f27b4060faa2..42aec8612f37 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -796,6 +796,7 @@ drm_priv_to_bridge(struct drm_private_obj *priv)
}
void drm_bridge_add(struct drm_bridge *bridge);
+int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge);
void drm_bridge_remove(struct drm_bridge *bridge);
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
struct drm_bridge *previous,
diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h
index 7ffbd9f7bfc7..49649eb8447e 100644
--- a/include/drm/drm_displayid.h
+++ b/include/drm/drm_displayid.h
@@ -25,7 +25,7 @@
#include <linux/types.h>
#include <linux/bits.h>
-struct edid;
+struct drm_edid;
#define VESA_IEEE_OUI 0x3a0292
@@ -141,7 +141,7 @@ struct displayid_vesa_vendor_specific_block {
/* DisplayID iteration */
struct displayid_iter {
- const struct edid *edid;
+ const struct drm_edid *drm_edid;
const u8 *section;
int length;
@@ -149,7 +149,7 @@ struct displayid_iter {
int ext_index;
};
-void displayid_iter_edid_begin(const struct edid *edid,
+void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
struct displayid_iter *iter);
const struct displayid_block *
__displayid_iter_next(struct displayid_iter *iter);
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b2756753370b..95ac09ef41b2 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -28,6 +28,7 @@
#include <drm/drm_mode.h>
struct drm_device;
+struct drm_edid;
struct i2c_adapter;
#define EDID_LENGTH 128
@@ -497,6 +498,22 @@ static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
}
/**
+ * drm_edid_decode_mfg_id - Decode the manufacturer ID
+ * @mfg_id: The manufacturer ID
+ * @vend: A 4-byte buffer to store the 3-letter vendor string plus a '\0'
+ * termination
+ */
+static inline const char *drm_edid_decode_mfg_id(u16 mfg_id, char vend[4])
+{
+ vend[0] = '@' + ((mfg_id >> 10) & 0x1f);
+ vend[1] = '@' + ((mfg_id >> 5) & 0x1f);
+ vend[2] = '@' + ((mfg_id >> 0) & 0x1f);
+ vend[3] = '\0';
+
+ return vend;
+}
+
+/**
* drm_edid_encode_panel_id - Encode an ID for matching against drm_edid_get_panel_id()
* @vend_chr_0: First character of the vendor string.
* @vend_chr_1: Second character of the vendor string.
@@ -536,10 +553,7 @@ static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
static inline void drm_edid_decode_panel_id(u32 panel_id, char vend[4], u16 *product_id)
{
*product_id = (u16)(panel_id & 0xffff);
- vend[0] = '@' + ((panel_id >> 26) & 0x1f);
- vend[1] = '@' + ((panel_id >> 21) & 0x1f);
- vend[2] = '@' + ((panel_id >> 16) & 0x1f);
- vend[3] = '\0';
+ drm_edid_decode_mfg_id(panel_id >> 16, vend);
}
bool drm_probe_ddc(struct i2c_adapter *adapter);
@@ -578,8 +592,9 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
struct drm_display_mode *
drm_display_mode_from_cea_vic(struct drm_device *dev,
u8 video_code);
-const u8 *drm_find_edid_extension(const struct edid *edid,
- int ext_id, int *ext_index);
+/* Interface based on struct drm_edid */
+const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
+ int ext_id, int *ext_index);
#endif /* __DRM_EDID_H__ */
diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h
index 1091e4fa08cb..d302521f3dd4 100644
--- a/include/drm/drm_gem_framebuffer_helper.h
+++ b/include/drm/drm_gem_framebuffer_helper.h
@@ -4,8 +4,6 @@
#include <linux/dma-buf.h>
#include <linux/iosys-map.h>
-#include <drm/drm_fourcc.h>
-
struct drm_afbc_framebuffer;
struct drm_device;
struct drm_fb_helper_surface_size;
@@ -39,11 +37,9 @@ struct drm_framebuffer *
drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd);
-int drm_gem_fb_vmap(struct drm_framebuffer *fb,
- struct iosys_map map[static DRM_FORMAT_MAX_PLANES],
- struct iosys_map data[DRM_FORMAT_MAX_PLANES]);
-void drm_gem_fb_vunmap(struct drm_framebuffer *fb,
- struct iosys_map map[static DRM_FORMAT_MAX_PLANES]);
+int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
+ struct iosys_map *data);
+void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map);
int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir);
void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir);
diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h
index 48300aa6ca71..c80cab7a53b7 100644
--- a/include/drm/drm_probe_helper.h
+++ b/include/drm/drm_probe_helper.h
@@ -26,4 +26,6 @@ void drm_kms_helper_poll_disable(struct drm_device *dev);
void drm_kms_helper_poll_enable(struct drm_device *dev);
bool drm_kms_helper_is_poll_worker(void);
+int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector);
+
#endif
diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h
index 0fca8f38bee4..addb135eeea6 100644
--- a/include/drm/gpu_scheduler.h
+++ b/include/drm/gpu_scheduler.h
@@ -28,7 +28,7 @@
#include <linux/dma-fence.h>
#include <linux/completion.h>
#include <linux/xarray.h>
-#include <linux/irq_work.h>
+#include <linux/workqueue.h>
#define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000)
@@ -295,7 +295,7 @@ struct drm_sched_job {
*/
union {
struct dma_fence_cb finish_cb;
- struct irq_work work;
+ struct work_struct work;
};
uint64_t id;
diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h
index 77e335a1bcac..390de1ee9d35 100644
--- a/include/linux/dma-fence-unwrap.h
+++ b/include/linux/dma-fence-unwrap.h
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * fence-chain: chain fences together in a timeline
- *
* Copyright (C) 2022 Advanced Micro Devices, Inc.
* Authors:
* Christian König <christian.koenig@amd.com>
@@ -10,8 +8,7 @@
#ifndef __LINUX_DMA_FENCE_UNWRAP_H
#define __LINUX_DMA_FENCE_UNWRAP_H
-#include <linux/dma-fence-chain.h>
-#include <linux/dma-fence-array.h>
+struct dma_fence;
/**
* struct dma_fence_unwrap - cursor into the container structure
@@ -33,50 +30,9 @@ struct dma_fence_unwrap {
unsigned int index;
};
-/* Internal helper to start new array iteration, don't use directly */
-static inline struct dma_fence *
-__dma_fence_unwrap_array(struct dma_fence_unwrap * cursor)
-{
- cursor->array = dma_fence_chain_contained(cursor->chain);
- cursor->index = 0;
- return dma_fence_array_first(cursor->array);
-}
-
-/**
- * dma_fence_unwrap_first - return the first fence from fence containers
- * @head: the entrypoint into the containers
- * @cursor: current position inside the containers
- *
- * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
- * first fence.
- */
-static inline struct dma_fence *
-dma_fence_unwrap_first(struct dma_fence *head, struct dma_fence_unwrap *cursor)
-{
- cursor->chain = dma_fence_get(head);
- return __dma_fence_unwrap_array(cursor);
-}
-
-/**
- * dma_fence_unwrap_next - return the next fence from a fence containers
- * @cursor: current position inside the containers
- *
- * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
- * the next fence from them.
- */
-static inline struct dma_fence *
-dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
-{
- struct dma_fence *tmp;
-
- ++cursor->index;
- tmp = dma_fence_array_next(cursor->array, cursor->index);
- if (tmp)
- return tmp;
-
- cursor->chain = dma_fence_chain_walk(cursor->chain);
- return __dma_fence_unwrap_array(cursor);
-}
+struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head,
+ struct dma_fence_unwrap *cursor);
+struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor);
/**
* dma_fence_unwrap_for_each - iterate over all fences in containers
@@ -87,9 +43,37 @@ dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
* Unwrap dma_fence_chain and dma_fence_array containers and deep dive into all
* potential fences in them. If @head is just a normal fence only that one is
* returned.
+ *
+ * Note that signalled fences are opportunistically filtered out, which
+ * means the iteration is potentially over no fence at all.
*/
#define dma_fence_unwrap_for_each(fence, cursor, head) \
for (fence = dma_fence_unwrap_first(head, cursor); fence; \
- fence = dma_fence_unwrap_next(cursor))
+ fence = dma_fence_unwrap_next(cursor)) \
+ if (!dma_fence_is_signaled(fence))
+
+struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
+ struct dma_fence **fences,
+ struct dma_fence_unwrap *cursors);
+
+/**
+ * dma_fence_unwrap_merge - unwrap and merge fences
+ *
+ * All fences given as parameters are unwrapped and merged back together as flat
+ * dma_fence_array. Useful if multiple containers need to be merged together.
+ *
+ * Implemented as a macro to allocate the necessary arrays on the stack and
+ * account the stack frame size to the caller.
+ *
+ * Returns NULL on memory allocation failure, a dma_fence object representing
+ * all the given fences otherwise.
+ */
+#define dma_fence_unwrap_merge(...) \
+ ({ \
+ struct dma_fence *__f[] = { __VA_ARGS__ }; \
+ struct dma_fence_unwrap __c[ARRAY_SIZE(__f)]; \
+ \
+ __dma_fence_unwrap_merge(ARRAY_SIZE(__f), __f, __c); \
+ })
#endif
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 2892145468c9..07fcd0e56682 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -511,7 +511,6 @@ struct fb_info {
} *apertures;
bool skip_vt_switch; /* no VT switch on suspend/resume required */
- bool forced_out; /* set when being removed by another driver */
};
static inline struct apertures_struct *alloc_apertures(unsigned int max_num) {