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 -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmKgbTkACgkQaA3BHVML eiPmTAf/dI/kxzrkPAEjAFPnvN9rEeTjoTR/A7L/XiRSflxuP9voRmIi8BqlYde8 pW7FAZBUonH8oX7w1mtM5VaSjIRaGezTNPQELfXnDdziDE7+934nBmfJUEZKKHm/ xMJrOg8Y49VTXnfxlFuZiWra+ntL+4LPvNaXNbnhGA2c7PkRWjv4BoS0GVT4N9G5 BPGvqHVFqC2R9NIP/TVA6RKmECEj1MD9Dtl0x85cde9e1ARGNm63J8R+mISQclJE ECeg45KI8GXl0j0DGS40bZxN97V0a3ZOekP7WsesoU8oYk/b9SJuLyIUlQsAj8gP BvQQ19DATNI3uu8ONRWebzrVYZlGAw== =EerK -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
0f95ee9a0c
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -35,7 +35,6 @@ required:
|
|||
- reg
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
- reset-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,17 +525,23 @@ 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)
|
||||
vres->base.start = 0;
|
||||
list_for_each_entry(block, &vres->blocks, link) {
|
||||
unsigned long start;
|
||||
|
||||
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);
|
||||
|
||||
vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
|
||||
|
||||
block = amdgpu_vram_mgr_first_block(&vres->blocks);
|
||||
if (!block) {
|
||||
r = -EINVAL;
|
||||
goto error_fini;
|
||||
}
|
||||
|
||||
vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT;
|
||||
|
||||
if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks))
|
||||
vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 | \
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ®_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, ®_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,
|
||||
|
|
|
@ -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(<9611uxc->work);
|
||||
lt9611uxc_audio_exit(lt9611uxc);
|
||||
drm_bridge_remove(<9611uxc->bridge);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
ret = devm_of_dp_aux_populate_bus(&ps_bridge->aux, ps8640_bridge_link_panel);
|
||||
|
||||
/* 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);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
drm_bridge_add(&ps_bridge->bridge);
|
||||
|
||||
ret = ps8640_bridge_host_attach(dev, ps_bridge);
|
||||
if (ret)
|
||||
goto err_bridge_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
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,
|
||||
|
|
|
@ -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,10 +1247,59 @@ static int tc_main_link_disable(struct tc_data *tc)
|
|||
return regmap_write(tc->regmap, DP0CTL, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
u32 value;
|
||||
|
||||
dev_dbg(tc->dev, "enable video stream\n");
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1370,19 +1400,14 @@ static int tc_edp_stream_enable(struct tc_data *tc)
|
|||
usleep_range(500, 1000);
|
||||
value |= VID_EN;
|
||||
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;
|
||||
else
|
||||
value |= DP0_VIDSRC_DPI_RX;
|
||||
ret = regmap_write(tc->regmap, SYSCTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
/* Set input interface */
|
||||
if (tc->input_connector_dsi)
|
||||
return tc_dsi_rx_enable(tc);
|
||||
else
|
||||
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);
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* As per docs of device_register(), call this instead
|
||||
* of kfree() directly for error cases.
|
||||
*/
|
||||
put_device(&aux_ep->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.
|
||||
*/
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
of_node_put(bus);
|
||||
aux_ep_with_data = kzalloc(sizeof(*aux_ep_with_data), GFP_KERNEL);
|
||||
if (!aux_ep_with_data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_did_set_populated;
|
||||
}
|
||||
|
||||
aux_ep_with_data->done_probing = done_probing;
|
||||
|
||||
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));
|
||||
|
||||
ret = device_register(&aux_ep->dev);
|
||||
if (ret) {
|
||||
dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
static void of_dp_aux_depopulate_ep_devices_void(void *data)
|
||||
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_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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
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;
|
||||
|
||||
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 {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
}
|
||||
if (count != 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
prune:
|
||||
drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
|
||||
drm_mode_prune_invalid(dev, &connector->modes, true);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
drm_mode_prune_invalid(dev, &connector->modes, true);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,265 +111,89 @@ 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;
|
||||
resource_size_t start, len;
|
||||
struct resource *res;
|
||||
|
||||
/* BAR 1 contains registers */
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
/* BAR 0 is VRAM */
|
||||
|
||||
start = pci_resource_start(pdev, 0);
|
||||
len = pci_resource_len(pdev, 0);
|
||||
|
||||
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;
|
||||
}
|
||||
mdev->vram_res = res;
|
||||
|
||||
/* 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));
|
||||
|
||||
mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res));
|
||||
if (!mdev->vram)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgag200_device_init(struct mga_device *mdev, enum mga_type type,
|
||||
const struct mgag200_device_info *info)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
u8 crtcext3, misc;
|
||||
int ret;
|
||||
|
||||
mdev->info = info;
|
||||
mdev->type = type;
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
mdev->rmmio = pcim_iomap(pdev, 1, 0);
|
||||
if (mdev->rmmio == NULL)
|
||||
return -ENOMEM;
|
||||
mutex_lock(&mdev->rmmio_lock);
|
||||
|
||||
RREG_ECRT(0x03, crtcext3);
|
||||
crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
|
||||
WREG_ECRT(0x03, crtcext3);
|
||||
|
||||
WREG_ECRT(0x04, 0x00);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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(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);
|
||||
|
||||
vfree(bios);
|
||||
out:
|
||||
pci_unmap_rom(pdev, rom);
|
||||
}
|
||||
|
||||
static void mgag200_g200se_init_unique_id(struct mga_device *mdev)
|
||||
{
|
||||
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;
|
||||
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->type = type;
|
||||
|
||||
ret = mgag200_regs_init(mdev);
|
||||
if (ret)
|
||||
return ERR_PTR(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);
|
||||
|
||||
ret = mgag200_mm_init(mdev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = mgag200_modeset_init(mdev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return mdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI driver
|
||||
*/
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 MGAG200_TYPE_MASK (0x000000ff)
|
||||
#define MGAG200_FLAG_MASK (0x00ffff00)
|
||||
|
||||
#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B)
|
||||
|
||||
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 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;
|
||||
const struct mgag200_device_info *info;
|
||||
|
||||
struct resource *rmmio_res;
|
||||
void __iomem *rmmio;
|
||||
struct mutex rmmio_lock; /* Protects access to rmmio */
|
||||
|
||||
struct mga_mc mc;
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
fb = mdev->display_pipe.plane.state->fb;
|
||||
|
||||
r_ptr = crtc->gamma_store;
|
||||
g_ptr = r_ptr + crtc->gamma_size;
|
||||
b_ptr = g_ptr + crtc->gamma_size;
|
||||
|
||||
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 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);
|
||||
}
|
||||
return;
|
||||
/* 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
/* 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,99 +690,53 @@ static void mgag200_disable_display(struct mga_device *mdev)
|
|||
* Connector
|
||||
*/
|
||||
|
||||
static int mga_vga_get_modes(struct drm_connector *connector)
|
||||
static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct mga_connector *mga_connector = to_mga_connector(connector);
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
struct mga_device *mdev = to_mga_device(connector->dev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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)
|
||||
static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
|
||||
.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 = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple Display Pipe
|
||||
*/
|
||||
|
||||
static enum drm_mode_status
|
||||
mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
uint32_t total_area, divisor;
|
||||
uint64_t active_area, pixels_per_second, bandwidth;
|
||||
uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
|
||||
struct mga_device *mdev = to_mga_device(pipe->crtc.dev);
|
||||
const struct mgag200_device_info *info = mdev->info;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
/*
|
||||
* 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) {
|
||||
|
@ -771,78 +750,6 @@ static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
|
|||
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;
|
||||
}
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
|
||||
.get_modes = mga_vga_get_modes,
|
||||
.mode_valid = mga_vga_mode_valid,
|
||||
};
|
||||
|
||||
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,
|
||||
.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
|
||||
*/
|
||||
|
||||
static enum drm_mode_status
|
||||
mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
.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,
|
||||
};
|
||||
|
||||
static unsigned int mgag200_preferred_depth(struct mga_device *mdev)
|
||||
{
|
||||
if (IS_G200_SE(mdev) && mdev->vram_fb_available < (2048*1024))
|
||||
return 16;
|
||||
else
|
||||
return 32;
|
||||
}
|
||||
|
||||
int mgag200_modeset_init(struct mga_device *mdev)
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 },
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(®istration_lock);
|
||||
platform_device_unregister(to_platform_device(device));
|
||||
mutex_lock(®istration_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(®istration_lock);
|
||||
mutex_lock(®istration_lock);
|
||||
do_unregister_framebuffer(fb_info);
|
||||
if (!forced_out)
|
||||
mutex_unlock(®istration_lock);
|
||||
mutex_unlock(®istration_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_framebuffer);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue