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:
Daniel Vetter 2022-06-15 19:12:17 +02:00
commit 0f95ee9a0c
110 changed files with 4070 additions and 2324 deletions

View File

@ -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>;

View File

@ -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>;

View File

@ -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>;
};
};
};
};
};

View File

@ -35,7 +35,6 @@ required:
- reg
- avdd-supply
- dvdd-supply
- reset-gpios
additionalProperties: false

View File

@ -14,7 +14,8 @@ properties:
pattern: '^gpu@[a-f0-9]+$'
compatible:
items:
oneOf:
- items:
- enum:
- amlogic,meson-g12a-mali
- mediatek,mt8183-mali
@ -24,6 +25,10 @@ properties:
- 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

View File

@ -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
----------------------

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
if (sync_file_set_fence(sync_file, fences, index) < 0)
goto err_put_fences;
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:
fence = dma_fence_unwrap_merge(a->fence, b->fence);
if (!fence) {
fput(sync_file->file);
return NULL;
}
sync_file->fence = fence;
strlcpy(sync_file->user_name, name, sizeof(sync_file->user_name));
return sync_file;
}
static int sync_file_release(struct inode *inode, struct file *file)

View 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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 | \

View File

@ -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) */

View File

@ -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,

View File

@ -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);

View File

@ -578,15 +578,13 @@ static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode)
}
/* connector funcs */
static enum drm_connector_status
lt9611_connector_detect(struct drm_connector *connector, bool force)
static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611)
{
struct lt9611 *lt9611 = connector_to_lt9611(connector);
unsigned int reg_val = 0;
int connected = 0;
regmap_read(lt9611->regmap, 0x825e, &reg_val);
connected = (reg_val & BIT(0));
connected = (reg_val & (BIT(2) | BIT(0)));
lt9611->status = connected ? connector_status_connected :
connector_status_disconnected;
@ -594,6 +592,12 @@ lt9611_connector_detect(struct drm_connector *connector, bool force)
return lt9611->status;
}
static enum drm_connector_status
lt9611_connector_detect(struct drm_connector *connector, bool force)
{
return __lt9611_detect(connector_to_lt9611(connector));
}
static int lt9611_read_edid(struct lt9611 *lt9611)
{
unsigned int temp;
@ -893,17 +897,7 @@ static void lt9611_bridge_mode_set(struct drm_bridge *bridge,
static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
unsigned int reg_val = 0;
int connected;
regmap_read(lt9611->regmap, 0x825e, &reg_val);
connected = reg_val & BIT(0);
lt9611->status = connected ? connector_status_connected :
connector_status_disconnected;
return lt9611->status;
return __lt9611_detect(bridge_to_lt9611(bridge));
}
static struct edid *lt9611_bridge_get_edid(struct drm_bridge *bridge,

View File

@ -982,7 +982,7 @@ static int lt9611uxc_remove(struct i2c_client *client)
struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client);
disable_irq(client->irq);
flush_scheduled_work();
cancel_work_sync(&lt9611uxc->work);
lt9611uxc_audio_exit(lt9611uxc);
drm_bridge_remove(&lt9611uxc->bridge);

View File

@ -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);

View File

@ -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,

View File

@ -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
} 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);

View File

@ -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");

View File

@ -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,6 +63,31 @@ static int dp_aux_ep_probe(struct device *dev)
ret = aux_ep_drv->probe(aux_ep);
if (ret)
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,65 +202,88 @@ 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;
if (of_node_test_and_set_flag(np, OF_POPULATED)) {
dev_err(aux->dev, "DP AUX EP device already populated\n");
ret = -EINVAL;
goto err_did_get_np;
}
aux_ep_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;
@ -235,8 +293,6 @@ int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
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
@ -244,45 +300,52 @@ int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
*/
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.
*/
goto err_did_set_populated;
}
}
of_node_put(bus);
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)
{

View File

@ -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
*

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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",

View File

@ -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 {
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);
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);
}
prune:
drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
exit:
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
@ -964,3 +1004,39 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
return changed;
}
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
/**
* drm_connector_helper_get_modes_from_ddc - Updates the connector's EDID
* property from the connector's
* DDC channel
* @connector: The connector
*
* Returns:
* The number of detected display modes.
*
* Uses a connector's DDC channel to retrieve EDID data and update the
* connector's EDID property and display modes. Drivers can use this
* function to implement struct &drm_connector_helper_funcs.get_modes
* for connectors with a DDC channel.
*/
int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector)
{
struct edid *edid;
int count = 0;
if (!connector->ddc)
return 0;
edid = drm_get_edid(connector, connector->ddc);
// clears property if EDID is NULL
drm_connector_update_edid_property(connector, edid);
if (edid) {
count = drm_add_edid_modes(connector, edid);
kfree(edid);
}
return count;
}
EXPORT_SYMBOL(drm_connector_helper_get_modes_from_ddc);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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,
ret = drm_connector_init_with_ddc(dev, connector,
&cdv_intel_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
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);
}

View File

@ -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);
}

View File

@ -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,
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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -78,13 +78,14 @@ struct psb_intel_mode_device {
uint32_t saveBLC_PWM_CTL;
};
struct psb_intel_i2c_chan {
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 i2c_adapter adapter;
struct i2c_algo_bit_data algo;
u8 slave_addr;
};
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);

View File

@ -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);
}

View File

@ -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);
}
return 0;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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 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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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++;
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);
}
} 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);
}
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++) {
/* 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);
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;
}
}
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)
/*
* 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 > 1200)
if (mode->vdisplay > info->max_vdisplay)
return MODE_VIRTUAL_Y;
if (mga_vga_calculate_mode_bandwidth(mode, bpp)
> (24400 * 1024))
return MODE_BANDWIDTH;
} else if (unique_rev_id == 0x02) {
if (mode->hdisplay > 1920)
return MODE_VIRTUAL_X;
if (mode->vdisplay > 1200)
return MODE_VIRTUAL_Y;
if (mga_vga_calculate_mode_bandwidth(mode, bpp)
> (30100 * 1024))
return MODE_BANDWIDTH;
} else {
if (mga_vga_calculate_mode_bandwidth(mode, bpp)
> (55000 * 1024))
return MODE_BANDWIDTH;
}
} else if (mdev->type == G200_WB) {
if (mode->hdisplay > 1280)
return MODE_VIRTUAL_X;
if (mode->vdisplay > 1024)
return MODE_VIRTUAL_Y;
if (mga_vga_calculate_mode_bandwidth(mode, bpp) >
(31877 * 1024))
return MODE_BANDWIDTH;
} else if (mdev->type == G200_EV &&
(mga_vga_calculate_mode_bandwidth(mode, bpp)
> (32700 * 1024))) {
return MODE_BANDWIDTH;
} else if (mdev->type == G200_EH &&
(mga_vga_calculate_mode_bandwidth(mode, bpp)
> (37500 * 1024))) {
return MODE_BANDWIDTH;
} else if (mdev->type == G200_ER &&
(mga_vga_calculate_mode_bandwidth(mode,
bpp) > (55000 * 1024))) {
return MODE_BANDWIDTH;
}
if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
(mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
@ -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,
.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;

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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_pwr);
if (ret)
goto err_clk_pwr;
@ -70,7 +68,6 @@ 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);
err_clk_ref:
clk_disable_unprepare(tdev->clk);
@ -87,7 +84,6 @@ 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);
udelay(10);

View File

@ -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,

View File

@ -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;
}

View File

@ -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));

View File

@ -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

View File

@ -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");

View File

@ -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;
}

View File

@ -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 },
{}
};

View File

@ -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)
{

View File

@ -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)

View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 *

View File

@ -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 = {

View File

@ -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);

View File

@ -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;

View File

@ -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 |

View File

@ -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)
{

View File

@ -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");

View File

@ -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

View File

@ -1555,6 +1555,7 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
{
int i;
restart_removal:
/* check all firmware fbs and kick off if the base addr overlaps */
for_each_registered_fb(i) {
struct apertures_struct *gen_aper;
@ -1587,12 +1588,23 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
pr_warn("fb%d: no device set\n", i);
do_unregister_framebuffer(registered_fb[i]);
} else if (dev_is_platform(device)) {
registered_fb[i]->forced_out = true;
/*
* Drop the lock because if the device is unregistered, its
* driver will call to unregister_framebuffer(), that takes
* this lock.
*/
mutex_unlock(&registration_lock);
platform_device_unregister(to_platform_device(device));
mutex_lock(&registration_lock);
} else {
pr_warn("fb%d: cannot remove device\n", i);
do_unregister_framebuffer(registered_fb[i]);
}
/*
* Restart the removal loop now that the device has been
* unregistered and its associated framebuffer gone.
*/
goto restart_removal;
}
}
}
@ -1852,12 +1864,8 @@ EXPORT_SYMBOL(register_framebuffer);
void
unregister_framebuffer(struct fb_info *fb_info)
{
bool forced_out = fb_info->forced_out;
if (!forced_out)
mutex_lock(&registration_lock);
do_unregister_framebuffer(fb_info);
if (!forced_out)
mutex_unlock(&registration_lock);
}
EXPORT_SYMBOL(unregister_framebuffer);

View File

@ -44,9 +44,37 @@ static inline struct dp_aux_ep_driver *to_dp_aux_ep_drv(struct device_driver *dr
return container_of(drv, struct dp_aux_ep_driver, driver);
}
int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux);
int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
int of_dp_aux_populate_bus(struct drm_dp_aux *aux,
int (*done_probing)(struct drm_dp_aux *aux));
void of_dp_aux_depopulate_bus(struct drm_dp_aux *aux);
int devm_of_dp_aux_populate_bus(struct drm_dp_aux *aux,
int (*done_probing)(struct drm_dp_aux *aux));
/* Deprecated versions of the above functions. To be removed when no callers. */
static inline int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
{
int ret;
ret = of_dp_aux_populate_bus(aux, NULL);
/* New API returns -ENODEV for no child case; adapt to old assumption */
return (ret != -ENODEV) ? ret : 0;
}
static inline int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
{
int ret;
ret = devm_of_dp_aux_populate_bus(aux, NULL);
/* New API returns -ENODEV for no child case; adapt to old assumption */
return (ret != -ENODEV) ? ret : 0;
}
static inline void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux)
{
of_dp_aux_depopulate_bus(aux);
}
#define dp_aux_dp_driver_register(aux_ep_drv) \
__dp_aux_dp_driver_register(aux_ep_drv, THIS_MODULE)

Some files were not shown because too many files have changed in this diff Show More