Merge tag 'drm-misc-next-2022-04-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.19-rc1

UAPI Changes:

Cross-subsystem Changes:
  - of: Create a platform_device for offb

Core Changes:
  - edid: block read refactoring
  - ttm: Add common debugfs code for resource managers

Driver Changes:
  - bridges:
    - adv7611: Enable DRM_BRIDGE_OP_HPD if there's an interrupt
    - anx7625: Fill ELD if no monitor is connected
    - dw_hdmi: Add General Parallel Audio support
    - icn6211: Add data-lanes DT property
    - new driver: Lontium LT9211
  - nouveau: make some structures static
  - tidss: Reset DISPC on startup
  - solomon: SPI Support and DT bindings improvements

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20220421065948.2pyp3j7acxtl6pz5@houat
This commit is contained in:
Dave Airlie
2022-04-22 06:43:52 +10:00
146 changed files with 4027 additions and 1845 deletions
@@ -41,10 +41,26 @@ properties:
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
$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:
@@ -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/lontium,lt9211.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Lontium LT9211 DSI/LVDS/DPI to DSI/LVDS/DPI bridge.
maintainers:
- Marek Vasut <marex@denx.de>
description: |
The LT9211 are bridge devices which convert Single/Dual-Link DSI/LVDS
or Single DPI to Single/Dual-Link DSI/LVDS or Single DPI.
properties:
compatible:
enum:
- lontium,lt9211
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
description: GPIO connected to active high RESET pin.
vccio-supply:
description: Regulator for 1.8V IO power.
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description:
Primary MIPI DSI port-1 for MIPI input or
LVDS port-1 for LVDS input or DPI input.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description:
Additional MIPI port-2 for MIPI input or LVDS port-2
for LVDS input. Used in combination with primary
port-1 to drive higher resolution displays
port@2:
$ref: /schemas/graph.yaml#/properties/port
description:
Primary MIPI DSI port-1 for MIPI output or
LVDS port-1 for LVDS output or DPI output.
port@3:
$ref: /schemas/graph.yaml#/properties/port
description:
Additional MIPI port-2 for MIPI output or LVDS port-2
for LVDS output. Used in combination with primary
port-1 to drive higher resolution displays.
required:
- port@0
- port@2
required:
- compatible
- reg
- vccio-supply
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
hdmi-bridge@3b {
compatible = "lontium,lt9211";
reg = <0x3b>;
reset-gpios = <&tlmm 128 GPIO_ACTIVE_HIGH>;
interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>;
vccio-supply = <&lt9211_1v8>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@2 {
reg = <2>;
endpoint {
remote-endpoint = <&panel_in_lvds>;
};
};
};
};
};
...
@@ -12,11 +12,22 @@ maintainers:
properties:
compatible:
enum:
- solomon,ssd1305fb-i2c
- solomon,ssd1306fb-i2c
- solomon,ssd1307fb-i2c
- solomon,ssd1309fb-i2c
oneOf:
# Deprecated compatible strings
- items:
- enum:
- solomon,ssd1305fb-i2c
- solomon,ssd1306fb-i2c
- solomon,ssd1307fb-i2c
- solomon,ssd1309fb-i2c
deprecated: true
- items:
- enum:
- sinowealth,sh1106
- solomon,ssd1305
- solomon,ssd1306
- solomon,ssd1307
- solomon,ssd1309
reg:
maxItems: 1
@@ -27,9 +38,20 @@ properties:
reset-gpios:
maxItems: 1
# Only required for SPI
dc-gpios:
description:
GPIO connected to the controller's D/C# (Data/Command) pin,
that is needed for 4-wire SPI to tell the controller if the
data sent is for a command register or the display data RAM
maxItems: 1
vbat-supply:
description: The supply for VBAT
# Only required for SPI
spi-max-frequency: true
solomon,height:
$ref: /schemas/types.yaml#/definitions/uint32
default: 16
@@ -135,7 +157,21 @@ allOf:
properties:
compatible:
contains:
const: solomon,ssd1305fb-i2c
const: sinowealth,sh1106
then:
properties:
solomon,dclk-div:
default: 1
solomon,dclk-frq:
default: 5
- if:
properties:
compatible:
contains:
enum:
- solomon,ssd1305-i2c
- solomon,ssd1305
then:
properties:
solomon,dclk-div:
@@ -147,7 +183,9 @@ allOf:
properties:
compatible:
contains:
const: solomon,ssd1306fb-i2c
enum:
- solomon,ssd1306-i2c
- solomon,ssd1306
then:
properties:
solomon,dclk-div:
@@ -159,7 +197,9 @@ allOf:
properties:
compatible:
contains:
const: solomon,ssd1307fb-i2c
enum:
- solomon,ssd1307-i2c
- solomon,ssd1307
then:
properties:
solomon,dclk-div:
@@ -173,7 +213,9 @@ allOf:
properties:
compatible:
contains:
const: solomon,ssd1309fb-i2c
enum:
- solomon,ssd1309-i2c
- solomon,ssd1309
then:
properties:
solomon,dclk-div:
@@ -189,15 +231,15 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
ssd1307: oled@3c {
compatible = "solomon,ssd1307fb-i2c";
ssd1307_i2c: oled@3c {
compatible = "solomon,ssd1307";
reg = <0x3c>;
pwms = <&pwm 4 3000>;
reset-gpios = <&gpio2 7>;
};
ssd1306: oled@3d {
compatible = "solomon,ssd1306fb-i2c";
ssd1306_i2c: oled@3d {
compatible = "solomon,ssd1306";
reg = <0x3c>;
pwms = <&pwm 4 3000>;
reset-gpios = <&gpio2 7>;
@@ -207,3 +249,30 @@ examples:
solomon,lookup-table = /bits/ 8 <0x3f 0x3f 0x3f 0x3f>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
ssd1307_spi: oled@0 {
compatible = "solomon,ssd1307";
reg = <0x0>;
pwms = <&pwm 4 3000>;
reset-gpios = <&gpio2 7>;
dc-gpios = <&gpio2 8>;
spi-max-frequency = <10000000>;
};
ssd1306_spi: oled@1 {
compatible = "solomon,ssd1306";
reg = <0x1>;
pwms = <&pwm 4 3000>;
reset-gpios = <&gpio2 7>;
dc-gpios = <&gpio2 8>;
spi-max-frequency = <10000000>;
solomon,com-lrremap;
solomon,com-invdir;
solomon,com-offset = <32>;
solomon,lookup-table = /bits/ 8 <0x3f 0x3f 0x3f 0x3f>;
};
};
@@ -1130,6 +1130,8 @@ patternProperties:
description: Sinlinx Electronics Technology Co., LTD
"^sinovoip,.*":
description: SinoVoip Co., Ltd
"^sinowealth,.*":
description: SINO WEALTH Electronic Ltd.
"^sipeed,.*":
description: Shenzhen Sipeed Technology Co., Ltd.
"^sirf,.*":
+18 -4
View File
@@ -216,7 +216,8 @@ static bool dma_buf_poll_add_cb(struct dma_resv *resv, bool write,
struct dma_fence *fence;
int r;
dma_resv_for_each_fence(&cursor, resv, write, fence) {
dma_resv_for_each_fence(&cursor, resv, dma_resv_usage_rw(write),
fence) {
dma_fence_get(fence);
r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb);
if (!r)
@@ -660,12 +661,24 @@ static struct sg_table * __map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction direction)
{
struct sg_table *sg_table;
signed long ret;
sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
if (IS_ERR_OR_NULL(sg_table))
return sg_table;
if (!IS_ERR_OR_NULL(sg_table))
mangle_sg_table(sg_table);
if (!dma_buf_attachment_is_dynamic(attach)) {
ret = dma_resv_wait_timeout(attach->dmabuf->resv,
DMA_RESV_USAGE_KERNEL, true,
MAX_SCHEDULE_TIMEOUT);
if (ret < 0) {
attach->dmabuf->ops->unmap_dma_buf(attach, sg_table,
direction);
return ERR_PTR(ret);
}
}
mangle_sg_table(sg_table);
return sg_table;
}
@@ -1124,7 +1137,8 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
long ret;
/* Wait on any implicit rendering fences */
ret = dma_resv_wait_timeout(resv, write, true, MAX_SCHEDULE_TIMEOUT);
ret = dma_resv_wait_timeout(resv, dma_resv_usage_rw(write),
true, MAX_SCHEDULE_TIMEOUT);
if (ret < 0)
return ret;
+173 -228
View File
@@ -44,12 +44,12 @@
/**
* DOC: Reservation Object Overview
*
* The reservation object provides a mechanism to manage shared and
* exclusive fences associated with a buffer. A reservation object
* can have attached one exclusive fence (normally associated with
* write operations) or N shared fences (read operations). The RCU
* mechanism is used to protect read access to fences from locked
* write-side updates.
* The reservation object provides a mechanism to manage a container of
* dma_fence object associated with a resource. A reservation object
* can have any number of fences attaches to it. Each fence carries an usage
* parameter determining how the operation represented by the fence is using the
* resource. The RCU mechanism is used to protect read access to fences from
* locked write-side updates.
*
* See struct dma_resv for more details.
*/
@@ -57,39 +57,59 @@
DEFINE_WD_CLASS(reservation_ww_class);
EXPORT_SYMBOL(reservation_ww_class);
/* Mask for the lower fence pointer bits */
#define DMA_RESV_LIST_MASK 0x3
struct dma_resv_list {
struct rcu_head rcu;
u32 shared_count, shared_max;
struct dma_fence __rcu *shared[];
u32 num_fences, max_fences;
struct dma_fence __rcu *table[];
};
/**
* dma_resv_list_alloc - allocate fence list
* @shared_max: number of fences we need space for
*
/* Extract the fence and usage flags from an RCU protected entry in the list. */
static void dma_resv_list_entry(struct dma_resv_list *list, unsigned int index,
struct dma_resv *resv, struct dma_fence **fence,
enum dma_resv_usage *usage)
{
long tmp;
tmp = (long)rcu_dereference_check(list->table[index],
resv ? dma_resv_held(resv) : true);
*fence = (struct dma_fence *)(tmp & ~DMA_RESV_LIST_MASK);
if (usage)
*usage = tmp & DMA_RESV_LIST_MASK;
}
/* Set the fence and usage flags at the specific index in the list. */
static void dma_resv_list_set(struct dma_resv_list *list,
unsigned int index,
struct dma_fence *fence,
enum dma_resv_usage usage)
{
long tmp = ((long)fence) | usage;
RCU_INIT_POINTER(list->table[index], (struct dma_fence *)tmp);
}
/*
* Allocate a new dma_resv_list and make sure to correctly initialize
* shared_max.
* max_fences.
*/
static struct dma_resv_list *dma_resv_list_alloc(unsigned int shared_max)
static struct dma_resv_list *dma_resv_list_alloc(unsigned int max_fences)
{
struct dma_resv_list *list;
list = kmalloc(struct_size(list, shared, shared_max), GFP_KERNEL);
list = kmalloc(struct_size(list, table, max_fences), GFP_KERNEL);
if (!list)
return NULL;
list->shared_max = (ksize(list) - offsetof(typeof(*list), shared)) /
sizeof(*list->shared);
list->max_fences = (ksize(list) - offsetof(typeof(*list), table)) /
sizeof(*list->table);
return list;
}
/**
* dma_resv_list_free - free fence list
* @list: list to free
*
* Free a dma_resv_list and make sure to drop all references.
*/
/* Free a dma_resv_list and make sure to drop all references. */
static void dma_resv_list_free(struct dma_resv_list *list)
{
unsigned int i;
@@ -97,9 +117,12 @@ static void dma_resv_list_free(struct dma_resv_list *list)
if (!list)
return;
for (i = 0; i < list->shared_count; ++i)
dma_fence_put(rcu_dereference_protected(list->shared[i], true));
for (i = 0; i < list->num_fences; ++i) {
struct dma_fence *fence;
dma_resv_list_entry(list, i, NULL, &fence, NULL);
dma_fence_put(fence);
}
kfree_rcu(list, rcu);
}
@@ -110,10 +133,8 @@ static void dma_resv_list_free(struct dma_resv_list *list)
void dma_resv_init(struct dma_resv *obj)
{
ww_mutex_init(&obj->lock, &reservation_ww_class);
seqcount_ww_mutex_init(&obj->seq, &obj->lock);
RCU_INIT_POINTER(obj->fence, NULL);
RCU_INIT_POINTER(obj->fence_excl, NULL);
RCU_INIT_POINTER(obj->fences, NULL);
}
EXPORT_SYMBOL(dma_resv_init);
@@ -123,46 +144,32 @@ EXPORT_SYMBOL(dma_resv_init);
*/
void dma_resv_fini(struct dma_resv *obj)
{
struct dma_resv_list *fobj;
struct dma_fence *excl;
/*
* This object should be dead and all references must have
* been released to it, so no need to be protected with rcu.
*/
excl = rcu_dereference_protected(obj->fence_excl, 1);
if (excl)
dma_fence_put(excl);
fobj = rcu_dereference_protected(obj->fence, 1);
dma_resv_list_free(fobj);
dma_resv_list_free(rcu_dereference_protected(obj->fences, true));
ww_mutex_destroy(&obj->lock);
}
EXPORT_SYMBOL(dma_resv_fini);
static inline struct dma_fence *
dma_resv_excl_fence(struct dma_resv *obj)
/* Dereference the fences while ensuring RCU rules */
static inline struct dma_resv_list *dma_resv_fences_list(struct dma_resv *obj)
{
return rcu_dereference_check(obj->fence_excl, dma_resv_held(obj));
}
static inline struct dma_resv_list *dma_resv_shared_list(struct dma_resv *obj)
{
return rcu_dereference_check(obj->fence, dma_resv_held(obj));
return rcu_dereference_check(obj->fences, dma_resv_held(obj));
}
/**
* dma_resv_reserve_fences - Reserve space to add shared fences to
* a dma_resv.
* dma_resv_reserve_fences - Reserve space to add fences to a dma_resv object.
* @obj: reservation object
* @num_fences: number of fences we want to add
*
* Should be called before dma_resv_add_shared_fence(). Must
* be called with @obj locked through dma_resv_lock().
* Should be called before dma_resv_add_fence(). Must be called with @obj
* locked through dma_resv_lock().
*
* Note that the preallocated slots need to be re-reserved if @obj is unlocked
* at any time before calling dma_resv_add_shared_fence(). This is validated
* when CONFIG_DEBUG_MUTEXES is enabled.
* at any time before calling dma_resv_add_fence(). This is validated when
* CONFIG_DEBUG_MUTEXES is enabled.
*
* RETURNS
* Zero for success, or -errno
@@ -174,11 +181,11 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
dma_resv_assert_held(obj);
old = dma_resv_shared_list(obj);
if (old && old->shared_max) {
if ((old->shared_count + num_fences) <= old->shared_max)
old = dma_resv_fences_list(obj);
if (old && old->max_fences) {
if ((old->num_fences + num_fences) <= old->max_fences)
return 0;
max = max(old->shared_count + num_fences, old->shared_max * 2);
max = max(old->num_fences + num_fences, old->max_fences * 2);
} else {
max = max(4ul, roundup_pow_of_two(num_fences));
}
@@ -193,27 +200,27 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
* references from the old struct are carried over to
* the new.
*/
for (i = 0, j = 0, k = max; i < (old ? old->shared_count : 0); ++i) {
for (i = 0, j = 0, k = max; i < (old ? old->num_fences : 0); ++i) {
enum dma_resv_usage usage;
struct dma_fence *fence;
fence = rcu_dereference_protected(old->shared[i],
dma_resv_held(obj));
dma_resv_list_entry(old, i, obj, &fence, &usage);
if (dma_fence_is_signaled(fence))
RCU_INIT_POINTER(new->shared[--k], fence);
RCU_INIT_POINTER(new->table[--k], fence);
else
RCU_INIT_POINTER(new->shared[j++], fence);
dma_resv_list_set(new, j++, fence, usage);
}
new->shared_count = j;
new->num_fences = j;
/*
* We are not changing the effective set of fences here so can
* merely update the pointer to the new array; both existing
* readers and new readers will see exactly the same set of
* active (unsignaled) shared fences. Individual fences and the
* active (unsignaled) fences. Individual fences and the
* old array are protected by RCU and so will not vanish under
* the gaze of the rcu_read_lock() readers.
*/
rcu_assign_pointer(obj->fence, new);
rcu_assign_pointer(obj->fences, new);
if (!old)
return 0;
@@ -222,7 +229,7 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
for (i = k; i < max; ++i) {
struct dma_fence *fence;
fence = rcu_dereference_protected(new->shared[i],
fence = rcu_dereference_protected(new->table[i],
dma_resv_held(obj));
dma_fence_put(fence);
}
@@ -234,37 +241,39 @@ EXPORT_SYMBOL(dma_resv_reserve_fences);
#ifdef CONFIG_DEBUG_MUTEXES
/**
* dma_resv_reset_shared_max - reset shared fences for debugging
* dma_resv_reset_max_fences - reset fences for debugging
* @obj: the dma_resv object to reset
*
* Reset the number of pre-reserved shared slots to test that drivers do
* Reset the number of pre-reserved fence slots to test that drivers do
* correct slot allocation using dma_resv_reserve_fences(). See also
* &dma_resv_list.shared_max.
* &dma_resv_list.max_fences.
*/
void dma_resv_reset_shared_max(struct dma_resv *obj)
void dma_resv_reset_max_fences(struct dma_resv *obj)
{
struct dma_resv_list *fences = dma_resv_shared_list(obj);
struct dma_resv_list *fences = dma_resv_fences_list(obj);
dma_resv_assert_held(obj);
/* Test shared fence slot reservation */
/* Test fence slot reservation */
if (fences)
fences->shared_max = fences->shared_count;
fences->max_fences = fences->num_fences;
}
EXPORT_SYMBOL(dma_resv_reset_shared_max);
EXPORT_SYMBOL(dma_resv_reset_max_fences);
#endif
/**
* dma_resv_add_shared_fence - Add a fence to a shared slot
* dma_resv_add_fence - Add a fence to the dma_resv obj
* @obj: the reservation object
* @fence: the shared fence to add
* @fence: the fence to add
* @usage: how the fence is used, see enum dma_resv_usage
*
* Add a fence to a shared slot, @obj must be locked with dma_resv_lock(), and
* Add a fence to a slot, @obj must be locked with dma_resv_lock(), and
* dma_resv_reserve_fences() has been called.
*
* See also &dma_resv.fence for a discussion of the semantics.
*/
void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence)
void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
enum dma_resv_usage usage)
{
struct dma_resv_list *fobj;
struct dma_fence *old;
@@ -279,39 +288,36 @@ void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence)
*/
WARN_ON(dma_fence_is_container(fence));
fobj = dma_resv_shared_list(obj);
count = fobj->shared_count;
write_seqcount_begin(&obj->seq);
fobj = dma_resv_fences_list(obj);
count = fobj->num_fences;
for (i = 0; i < count; ++i) {
enum dma_resv_usage old_usage;
old = rcu_dereference_protected(fobj->shared[i],
dma_resv_held(obj));
if (old->context == fence->context ||
dma_fence_is_signaled(old))
goto replace;
dma_resv_list_entry(fobj, i, obj, &old, &old_usage);
if ((old->context == fence->context && old_usage >= usage) ||
dma_fence_is_signaled(old)) {
dma_resv_list_set(fobj, i, fence, usage);
dma_fence_put(old);
return;
}
}
BUG_ON(fobj->shared_count >= fobj->shared_max);
old = NULL;
BUG_ON(fobj->num_fences >= fobj->max_fences);
count++;
replace:
RCU_INIT_POINTER(fobj->shared[i], fence);
/* pointer update must be visible before we extend the shared_count */
smp_store_mb(fobj->shared_count, count);
write_seqcount_end(&obj->seq);
dma_fence_put(old);
dma_resv_list_set(fobj, i, fence, usage);
/* pointer update must be visible before we extend the num_fences */
smp_store_mb(fobj->num_fences, count);
}
EXPORT_SYMBOL(dma_resv_add_shared_fence);
EXPORT_SYMBOL(dma_resv_add_fence);
/**
* dma_resv_replace_fences - replace fences in the dma_resv obj
* @obj: the reservation object
* @context: the context of the fences to replace
* @replacement: the new fence to use instead
* @usage: how the new fence is used, see enum dma_resv_usage
*
* Replace fences with a specified context with a new fence. Only valid if the
* operation represented by the original fence has no longer access to the
@@ -321,107 +327,66 @@ EXPORT_SYMBOL(dma_resv_add_shared_fence);
* update fence which makes the resource inaccessible.
*/
void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context,
struct dma_fence *replacement)
struct dma_fence *replacement,
enum dma_resv_usage usage)
{
struct dma_resv_list *list;
struct dma_fence *old;
unsigned int i;
dma_resv_assert_held(obj);
write_seqcount_begin(&obj->seq);
list = dma_resv_fences_list(obj);
for (i = 0; list && i < list->num_fences; ++i) {
struct dma_fence *old;
old = dma_resv_excl_fence(obj);
if (old->context == context) {
RCU_INIT_POINTER(obj->fence_excl, dma_fence_get(replacement));
dma_fence_put(old);
}
list = dma_resv_shared_list(obj);
for (i = 0; list && i < list->shared_count; ++i) {
old = rcu_dereference_protected(list->shared[i],
dma_resv_held(obj));
dma_resv_list_entry(list, i, obj, &old, NULL);
if (old->context != context)
continue;
rcu_assign_pointer(list->shared[i], dma_fence_get(replacement));
dma_resv_list_set(list, i, replacement, usage);
dma_fence_put(old);
}
write_seqcount_end(&obj->seq);
}
EXPORT_SYMBOL(dma_resv_replace_fences);
/**
* dma_resv_add_excl_fence - Add an exclusive fence.
* @obj: the reservation object
* @fence: the exclusive fence to add
*
* Add a fence to the exclusive slot. @obj must be locked with dma_resv_lock().
* See also &dma_resv.fence_excl for a discussion of the semantics.
*/
void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence)
{
struct dma_fence *old_fence = dma_resv_excl_fence(obj);
dma_resv_assert_held(obj);
dma_fence_get(fence);
write_seqcount_begin(&obj->seq);
/* write_seqcount_begin provides the necessary memory barrier */
RCU_INIT_POINTER(obj->fence_excl, fence);
write_seqcount_end(&obj->seq);
dma_fence_put(old_fence);
}
EXPORT_SYMBOL(dma_resv_add_excl_fence);
/* Restart the iterator by initializing all the necessary fields, but not the
* relation to the dma_resv object. */
/* Restart the unlocked iteration by initializing the cursor object. */
static void dma_resv_iter_restart_unlocked(struct dma_resv_iter *cursor)
{
cursor->seq = read_seqcount_begin(&cursor->obj->seq);
cursor->index = -1;
cursor->shared_count = 0;
if (cursor->all_fences) {
cursor->fences = dma_resv_shared_list(cursor->obj);
if (cursor->fences)
cursor->shared_count = cursor->fences->shared_count;
} else {
cursor->fences = NULL;
}
cursor->index = 0;
cursor->num_fences = 0;
cursor->fences = dma_resv_fences_list(cursor->obj);
if (cursor->fences)
cursor->num_fences = cursor->fences->num_fences;
cursor->is_restarted = true;
}
/* Walk to the next not signaled fence and grab a reference to it */
static void dma_resv_iter_walk_unlocked(struct dma_resv_iter *cursor)
{
struct dma_resv *obj = cursor->obj;
if (!cursor->fences)
return;
do {
/* Drop the reference from the previous round */
dma_fence_put(cursor->fence);
if (cursor->index == -1) {
cursor->fence = dma_resv_excl_fence(obj);
cursor->index++;
if (!cursor->fence)
continue;
} else if (!cursor->fences ||
cursor->index >= cursor->shared_count) {
if (cursor->index >= cursor->num_fences) {
cursor->fence = NULL;
break;
} else {
struct dma_resv_list *fences = cursor->fences;
unsigned int idx = cursor->index++;
cursor->fence = rcu_dereference(fences->shared[idx]);
}
dma_resv_list_entry(cursor->fences, cursor->index++,
cursor->obj, &cursor->fence,
&cursor->fence_usage);
cursor->fence = dma_fence_get_rcu(cursor->fence);
if (!cursor->fence || !dma_fence_is_signaled(cursor->fence))
if (!cursor->fence) {
dma_resv_iter_restart_unlocked(cursor);
continue;
}
if (!dma_fence_is_signaled(cursor->fence) &&
cursor->usage >= cursor->fence_usage)
break;
} while (true);
}
@@ -444,7 +409,7 @@ struct dma_fence *dma_resv_iter_first_unlocked(struct dma_resv_iter *cursor)
do {
dma_resv_iter_restart_unlocked(cursor);
dma_resv_iter_walk_unlocked(cursor);
} while (read_seqcount_retry(&cursor->obj->seq, cursor->seq));
} while (dma_resv_fences_list(cursor->obj) != cursor->fences);
rcu_read_unlock();
return cursor->fence;
@@ -467,13 +432,13 @@ struct dma_fence *dma_resv_iter_next_unlocked(struct dma_resv_iter *cursor)
rcu_read_lock();
cursor->is_restarted = false;
restart = read_seqcount_retry(&cursor->obj->seq, cursor->seq);
restart = dma_resv_fences_list(cursor->obj) != cursor->fences;
do {
if (restart)
dma_resv_iter_restart_unlocked(cursor);
dma_resv_iter_walk_unlocked(cursor);
restart = true;
} while (read_seqcount_retry(&cursor->obj->seq, cursor->seq));
} while (dma_resv_fences_list(cursor->obj) != cursor->fences);
rcu_read_unlock();
return cursor->fence;
@@ -496,15 +461,9 @@ struct dma_fence *dma_resv_iter_first(struct dma_resv_iter *cursor)
dma_resv_assert_held(cursor->obj);
cursor->index = 0;
if (cursor->all_fences)
cursor->fences = dma_resv_shared_list(cursor->obj);
else
cursor->fences = NULL;
fence = dma_resv_excl_fence(cursor->obj);
if (!fence)
fence = dma_resv_iter_next(cursor);
cursor->fences = dma_resv_fences_list(cursor->obj);
fence = dma_resv_iter_next(cursor);
cursor->is_restarted = true;
return fence;
}
@@ -519,17 +478,22 @@ EXPORT_SYMBOL_GPL(dma_resv_iter_first);
*/
struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor)
{
unsigned int idx;
struct dma_fence *fence;
dma_resv_assert_held(cursor->obj);
cursor->is_restarted = false;
if (!cursor->fences || cursor->index >= cursor->fences->shared_count)
return NULL;
idx = cursor->index++;
return rcu_dereference_protected(cursor->fences->shared[idx],
dma_resv_held(cursor->obj));
do {
if (!cursor->fences ||
cursor->index >= cursor->fences->num_fences)
return NULL;
dma_resv_list_entry(cursor->fences, cursor->index++,
cursor->obj, &fence, &cursor->fence_usage);
} while (cursor->fence_usage > cursor->usage);
return fence;
}
EXPORT_SYMBOL_GPL(dma_resv_iter_next);
@@ -544,60 +508,43 @@ int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src)
{
struct dma_resv_iter cursor;
struct dma_resv_list *list;
struct dma_fence *f, *excl;
struct dma_fence *f;
dma_resv_assert_held(dst);
list = NULL;
excl = NULL;
dma_resv_iter_begin(&cursor, src, true);
dma_resv_iter_begin(&cursor, src, DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, f) {
if (dma_resv_iter_is_restarted(&cursor)) {
dma_resv_list_free(list);
dma_fence_put(excl);
if (cursor.shared_count) {
list = dma_resv_list_alloc(cursor.shared_count);
if (!list) {
dma_resv_iter_end(&cursor);
return -ENOMEM;
}
list->shared_count = 0;
} else {
list = NULL;
list = dma_resv_list_alloc(cursor.num_fences);
if (!list) {
dma_resv_iter_end(&cursor);
return -ENOMEM;
}
excl = NULL;
list->num_fences = 0;
}
dma_fence_get(f);
if (dma_resv_iter_is_exclusive(&cursor))
excl = f;
else
RCU_INIT_POINTER(list->shared[list->shared_count++], f);
dma_resv_list_set(list, list->num_fences++, f,
dma_resv_iter_usage(&cursor));
}
dma_resv_iter_end(&cursor);
write_seqcount_begin(&dst->seq);
excl = rcu_replace_pointer(dst->fence_excl, excl, dma_resv_held(dst));
list = rcu_replace_pointer(dst->fence, list, dma_resv_held(dst));
write_seqcount_end(&dst->seq);
list = rcu_replace_pointer(dst->fences, list, dma_resv_held(dst));
dma_resv_list_free(list);
dma_fence_put(excl);
return 0;
}
EXPORT_SYMBOL(dma_resv_copy_fences);
/**
* dma_resv_get_fences - Get an object's shared and exclusive
* dma_resv_get_fences - Get an object's fences
* fences without update side lock held
* @obj: the reservation object
* @write: true if we should return all fences
* @usage: controls which fences to include, see enum dma_resv_usage.
* @num_fences: the number of fences returned
* @fences: the array of fence ptrs returned (array is krealloc'd to the
* required size, and must be freed by caller)
@@ -605,7 +552,7 @@ EXPORT_SYMBOL(dma_resv_copy_fences);
* Retrieve all fences from the reservation object.
* Returns either zero or -ENOMEM.
*/
int dma_resv_get_fences(struct dma_resv *obj, bool write,
int dma_resv_get_fences(struct dma_resv *obj, enum dma_resv_usage usage,
unsigned int *num_fences, struct dma_fence ***fences)
{
struct dma_resv_iter cursor;
@@ -614,7 +561,7 @@ int dma_resv_get_fences(struct dma_resv *obj, bool write,
*num_fences = 0;
*fences = NULL;
dma_resv_iter_begin(&cursor, obj, write);
dma_resv_iter_begin(&cursor, obj, usage);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (dma_resv_iter_is_restarted(&cursor)) {
@@ -623,7 +570,7 @@ int dma_resv_get_fences(struct dma_resv *obj, bool write,
while (*num_fences)
dma_fence_put((*fences)[--(*num_fences)]);
count = cursor.shared_count + 1;
count = cursor.num_fences + 1;
/* Eventually re-allocate the array */
*fences = krealloc_array(*fences, count,
@@ -646,7 +593,7 @@ EXPORT_SYMBOL_GPL(dma_resv_get_fences);
/**
* dma_resv_get_singleton - Get a single fence for all the fences
* @obj: the reservation object
* @write: true if we should return all fences
* @usage: controls which fences to include, see enum dma_resv_usage.
* @fence: the resulting fence
*
* Get a single fence representing all the fences inside the resv object.
@@ -658,7 +605,7 @@ EXPORT_SYMBOL_GPL(dma_resv_get_fences);
*
* Returns 0 on success and negative error values on failure.
*/
int dma_resv_get_singleton(struct dma_resv *obj, bool write,
int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage,
struct dma_fence **fence)
{
struct dma_fence_array *array;
@@ -666,7 +613,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, bool write,
unsigned count;
int r;
r = dma_resv_get_fences(obj, write, &count, &fences);
r = dma_resv_get_fences(obj, usage, &count, &fences);
if (r)
return r;
@@ -697,10 +644,9 @@ int dma_resv_get_singleton(struct dma_resv *obj, bool write,
EXPORT_SYMBOL_GPL(dma_resv_get_singleton);
/**
* dma_resv_wait_timeout - Wait on reservation's objects
* shared and/or exclusive fences.
* dma_resv_wait_timeout - Wait on reservation's objects fences
* @obj: the reservation object
* @wait_all: if true, wait on all fences, else wait on just exclusive fence
* @usage: controls which fences to include, see enum dma_resv_usage.
* @intr: if true, do interruptible wait
* @timeout: timeout value in jiffies or zero to return immediately
*
@@ -710,14 +656,14 @@ EXPORT_SYMBOL_GPL(dma_resv_get_singleton);
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
* greater than zer on success.
*/
long dma_resv_wait_timeout(struct dma_resv *obj, bool wait_all, bool intr,
unsigned long timeout)
long dma_resv_wait_timeout(struct dma_resv *obj, enum dma_resv_usage usage,
bool intr, unsigned long timeout)
{
long ret = timeout ? timeout : 1;
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_iter_begin(&cursor, obj, wait_all);
dma_resv_iter_begin(&cursor, obj, usage);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
ret = dma_fence_wait_timeout(fence, intr, ret);
@@ -737,8 +683,7 @@ EXPORT_SYMBOL_GPL(dma_resv_wait_timeout);
* dma_resv_test_signaled - Test if a reservation object's fences have been
* signaled.
* @obj: the reservation object
* @test_all: if true, test all fences, otherwise only test the exclusive
* fence
* @usage: controls which fences to include, see enum dma_resv_usage.
*
* Callers are not required to hold specific locks, but maybe hold
* dma_resv_lock() already.
@@ -747,12 +692,12 @@ EXPORT_SYMBOL_GPL(dma_resv_wait_timeout);
*
* True if all fences signaled, else false.
*/
bool dma_resv_test_signaled(struct dma_resv *obj, bool test_all)
bool dma_resv_test_signaled(struct dma_resv *obj, enum dma_resv_usage usage)
{
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_iter_begin(&cursor, obj, test_all);
dma_resv_iter_begin(&cursor, obj, usage);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
dma_resv_iter_end(&cursor);
return false;
@@ -772,13 +717,13 @@ EXPORT_SYMBOL_GPL(dma_resv_test_signaled);
*/
void dma_resv_describe(struct dma_resv *obj, struct seq_file *seq)
{
static const char *usage[] = { "kernel", "write", "read", "bookkeep" };
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_for_each_fence(&cursor, obj, true, fence) {
dma_resv_for_each_fence(&cursor, obj, DMA_RESV_USAGE_READ, fence) {
seq_printf(seq, "\t%s fence:",
dma_resv_iter_is_exclusive(&cursor) ?
"Exclusive" : "Shared");
usage[dma_resv_iter_usage(&cursor)]);
dma_fence_describe(fence, seq);
}
}
+33 -78
View File
@@ -58,8 +58,9 @@ static int sanitycheck(void *arg)
return r;
}
static int test_signaling(void *arg, bool shared)
static int test_signaling(void *arg)
{
enum dma_resv_usage usage = (unsigned long)arg;
struct dma_resv resv;
struct dma_fence *f;
int r;
@@ -81,18 +82,14 @@ static int test_signaling(void *arg, bool shared)
goto err_unlock;
}
if (shared)
dma_resv_add_shared_fence(&resv, f);
else
dma_resv_add_excl_fence(&resv, f);
if (dma_resv_test_signaled(&resv, shared)) {
dma_resv_add_fence(&resv, f, usage);
if (dma_resv_test_signaled(&resv, usage)) {
pr_err("Resv unexpectedly signaled\n");
r = -EINVAL;
goto err_unlock;
}
dma_fence_signal(f);
if (!dma_resv_test_signaled(&resv, shared)) {
if (!dma_resv_test_signaled(&resv, usage)) {
pr_err("Resv not reporting signaled\n");
r = -EINVAL;
goto err_unlock;
@@ -105,18 +102,9 @@ err_free:
return r;
}
static int test_excl_signaling(void *arg)
{
return test_signaling(arg, false);
}
static int test_shared_signaling(void *arg)
{
return test_signaling(arg, true);
}
static int test_for_each(void *arg, bool shared)
static int test_for_each(void *arg)
{
enum dma_resv_usage usage = (unsigned long)arg;
struct dma_resv_iter cursor;
struct dma_fence *f, *fence;
struct dma_resv resv;
@@ -139,13 +127,10 @@ static int test_for_each(void *arg, bool shared)
goto err_unlock;
}
if (shared)
dma_resv_add_shared_fence(&resv, f);
else
dma_resv_add_excl_fence(&resv, f);
dma_resv_add_fence(&resv, f, usage);
r = -ENOENT;
dma_resv_for_each_fence(&cursor, &resv, shared, fence) {
dma_resv_for_each_fence(&cursor, &resv, usage, fence) {
if (!r) {
pr_err("More than one fence found\n");
r = -EINVAL;
@@ -156,7 +141,7 @@ static int test_for_each(void *arg, bool shared)
r = -EINVAL;
goto err_unlock;
}
if (dma_resv_iter_is_exclusive(&cursor) != !shared) {
if (dma_resv_iter_usage(&cursor) != usage) {
pr_err("Unexpected fence usage\n");
r = -EINVAL;
goto err_unlock;
@@ -176,18 +161,9 @@ err_free:
return r;
}
static int test_excl_for_each(void *arg)
{
return test_for_each(arg, false);
}
static int test_shared_for_each(void *arg)
{
return test_for_each(arg, true);
}
static int test_for_each_unlocked(void *arg, bool shared)
static int test_for_each_unlocked(void *arg)
{
enum dma_resv_usage usage = (unsigned long)arg;
struct dma_resv_iter cursor;
struct dma_fence *f, *fence;
struct dma_resv resv;
@@ -211,14 +187,11 @@ static int test_for_each_unlocked(void *arg, bool shared)
goto err_free;
}
if (shared)
dma_resv_add_shared_fence(&resv, f);
else
dma_resv_add_excl_fence(&resv, f);
dma_resv_add_fence(&resv, f, usage);
dma_resv_unlock(&resv);
r = -ENOENT;
dma_resv_iter_begin(&cursor, &resv, shared);
dma_resv_iter_begin(&cursor, &resv, usage);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (!r) {
pr_err("More than one fence found\n");
@@ -234,7 +207,7 @@ static int test_for_each_unlocked(void *arg, bool shared)
r = -EINVAL;
goto err_iter_end;
}
if (dma_resv_iter_is_exclusive(&cursor) != !shared) {
if (dma_resv_iter_usage(&cursor) != usage) {
pr_err("Unexpected fence usage\n");
r = -EINVAL;
goto err_iter_end;
@@ -244,7 +217,7 @@ static int test_for_each_unlocked(void *arg, bool shared)
if (r == -ENOENT) {
r = -EINVAL;
/* That should trigger an restart */
cursor.seq--;
cursor.fences = (void*)~0;
} else if (r == -EINVAL) {
r = 0;
}
@@ -260,18 +233,9 @@ err_free:
return r;
}
static int test_excl_for_each_unlocked(void *arg)
{
return test_for_each_unlocked(arg, false);
}
static int test_shared_for_each_unlocked(void *arg)
{
return test_for_each_unlocked(arg, true);
}
static int test_get_fences(void *arg, bool shared)
static int test_get_fences(void *arg)
{
enum dma_resv_usage usage = (unsigned long)arg;
struct dma_fence *f, **fences = NULL;
struct dma_resv resv;
int r, i;
@@ -294,13 +258,10 @@ static int test_get_fences(void *arg, bool shared)
goto err_resv;
}
if (shared)
dma_resv_add_shared_fence(&resv, f);
else
dma_resv_add_excl_fence(&resv, f);
dma_resv_add_fence(&resv, f, usage);
dma_resv_unlock(&resv);
r = dma_resv_get_fences(&resv, shared, &i, &fences);
r = dma_resv_get_fences(&resv, usage, &i, &fences);
if (r) {
pr_err("get_fences failed\n");
goto err_free;
@@ -322,30 +283,24 @@ err_resv:
return r;
}
static int test_excl_get_fences(void *arg)
{
return test_get_fences(arg, false);
}
static int test_shared_get_fences(void *arg)
{
return test_get_fences(arg, true);
}
int dma_resv(void)
{
static const struct subtest tests[] = {
SUBTEST(sanitycheck),
SUBTEST(test_excl_signaling),
SUBTEST(test_shared_signaling),
SUBTEST(test_excl_for_each),
SUBTEST(test_shared_for_each),
SUBTEST(test_excl_for_each_unlocked),
SUBTEST(test_shared_for_each_unlocked),
SUBTEST(test_excl_get_fences),
SUBTEST(test_shared_get_fences),
SUBTEST(test_signaling),
SUBTEST(test_for_each),
SUBTEST(test_for_each_unlocked),
SUBTEST(test_get_fences),
};
enum dma_resv_usage usage;
int r;
spin_lock_init(&fence_lock);
return subtests(tests, NULL);
for (usage = DMA_RESV_USAGE_KERNEL; usage <= DMA_RESV_USAGE_BOOKKEEP;
++usage) {
r = subtests(tests, (void *)(unsigned long)usage);
if (r)
return r;
}
return 0;
}
+1
View File
@@ -280,6 +280,7 @@ config DRM_AMDGPU
select HWMON
select BACKLIGHT_CLASS_DEVICE
select INTERVAL_TREE
select DRM_BUDDY
help
Choose this option if you have a recent AMD Radeon graphics card.
@@ -263,7 +263,7 @@ static int amdgpu_amdkfd_remove_eviction_fence(struct amdgpu_bo *bo,
*/
replacement = dma_fence_get_stub();
dma_resv_replace_fences(bo->tbo.base.resv, ef->base.context,
replacement);
replacement, DMA_RESV_USAGE_READ);
dma_fence_put(replacement);
return 0;
}
@@ -2447,6 +2447,8 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)
struct amdgpu_bo *bo = mem->bo;
uint32_t domain = mem->domain;
struct kfd_mem_attachment *attachment;
struct dma_resv_iter cursor;
struct dma_fence *fence;
total_size += amdgpu_bo_size(bo);
@@ -2461,10 +2463,13 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)
goto validate_map_fail;
}
}
ret = amdgpu_sync_fence(&sync_obj, bo->tbo.moving);
if (ret) {
pr_debug("Memory eviction: Sync BO fence failed. Try again\n");
goto validate_map_fail;
dma_resv_for_each_fence(&cursor, bo->tbo.base.resv,
DMA_RESV_USAGE_KERNEL, fence) {
ret = amdgpu_sync_fence(&sync_obj, fence);
if (ret) {
pr_debug("Memory eviction: Sync BO fence failed. Try again\n");
goto validate_map_fail;
}
}
list_for_each_entry(attachment, &mem->attachments, list) {
if (!attachment->is_mapped)
@@ -34,7 +34,6 @@ struct amdgpu_fpriv;
struct amdgpu_bo_list_entry {
struct ttm_validate_buffer tv;
struct amdgpu_bo_va *bo_va;
struct dma_fence_chain *chain;
uint32_t priority;
struct page **user_pages;
bool user_invalidated;
+7 -48
View File
@@ -55,8 +55,8 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj));
p->uf_entry.priority = 0;
p->uf_entry.tv.bo = &bo->tbo;
/* One for TTM and one for the CS job */
p->uf_entry.tv.num_shared = 2;
/* One for TTM and two for the CS job */
p->uf_entry.tv.num_shared = 3;
drm_gem_object_put(gobj);
@@ -574,14 +574,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo);
e->bo_va = amdgpu_vm_bo_find(vm, bo);
if (bo->tbo.base.dma_buf && !amdgpu_bo_explicit_sync(bo)) {
e->chain = dma_fence_chain_alloc();
if (!e->chain) {
r = -ENOMEM;
goto error_validate;
}
}
}
/* Move fence waiting after getting reservation lock of
@@ -642,13 +634,8 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
}
error_validate:
if (r) {
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
dma_fence_chain_free(e->chain);
e->chain = NULL;
}
if (r)
ttm_eu_backoff_reservation(&p->ticket, &p->validated);
}
out:
return r;
}
@@ -688,17 +675,9 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error,
{
unsigned i;
if (error && backoff) {
struct amdgpu_bo_list_entry *e;
amdgpu_bo_list_for_each_entry(e, parser->bo_list) {
dma_fence_chain_free(e->chain);
e->chain = NULL;
}
if (error && backoff)
ttm_eu_backoff_reservation(&parser->ticket,
&parser->validated);
}
for (i = 0; i < parser->num_post_deps; i++) {
drm_syncobj_put(parser->post_deps[i].syncobj);
@@ -1272,29 +1251,9 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm);
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
struct dma_resv *resv = e->tv.bo->base.resv;
struct dma_fence_chain *chain = e->chain;
struct dma_resv_iter cursor;
struct dma_fence *fence;
if (!chain)
continue;
/*
* Temporary workaround dma_resv shortcommings by wrapping up
* the submission in a dma_fence_chain and add it as exclusive
* fence.
*
* TODO: Remove together with dma_resv rework.
*/
dma_resv_for_each_fence(&cursor, resv, false, fence) {
break;
}
dma_fence_chain_init(chain, fence, dma_fence_get(p->fence), 1);
rcu_assign_pointer(resv->fence_excl, &chain->base);
e->chain = NULL;
}
/* Make sure all BOs are remembered as writers */
amdgpu_bo_list_for_each_entry(e, p->bo_list)
e->tv.num_shared = 0;
ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence);
mutex_unlock(&p->adev->notifier_lock);
+1 -2
View File
@@ -200,8 +200,7 @@ int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
goto unpin;
}
/* TODO: Unify this with other drivers */
r = dma_resv_get_fences(new_abo->tbo.base.resv, true,
r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
&work->shared_count,
&work->shared);
if (unlikely(r != 0)) {
+1 -13
View File
@@ -102,21 +102,9 @@ static int amdgpu_dma_buf_pin(struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
int r;
/* pin buffer into GTT */
r = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
if (r)
return r;
if (bo->tbo.moving) {
r = dma_fence_wait(bo->tbo.moving, true);
if (r) {
amdgpu_bo_unpin(bo);
return r;
}
}
return 0;
return amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
}
/**
+2 -1
View File
@@ -526,7 +526,8 @@ int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
}
robj = gem_to_amdgpu_bo(gobj);
ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, timeout);
ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ,
true, timeout);
/* ret == 0 means not signaled,
* ret > 0 means signaled
+3 -2
View File
@@ -111,7 +111,7 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv,
struct dma_fence *fence;
int r;
r = dma_resv_get_singleton(resv, true, &fence);
r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence);
if (r)
goto fallback;
@@ -139,7 +139,8 @@ fallback:
/* Not enough memory for the delayed delete, as last resort
* block for all the fences to complete.
*/
dma_resv_wait_timeout(resv, true, false, MAX_SCHEDULE_TIMEOUT);
dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP,
false, MAX_SCHEDULE_TIMEOUT);
amdgpu_pasid_free(pasid);
}
+2 -2
View File
@@ -75,8 +75,8 @@ static bool amdgpu_mn_invalidate_gfx(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(mni, cur_seq);
r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
MAX_SCHEDULE_TIMEOUT);
r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP,
false, MAX_SCHEDULE_TIMEOUT);
mutex_unlock(&adev->notifier_lock);
if (r <= 0)
DRM_ERROR("(%ld) failed to wait for user bo\n", r);
+9 -12
View File
@@ -612,9 +612,8 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
if (unlikely(r))
goto fail_unreserve;
amdgpu_bo_fence(bo, fence, false);
dma_fence_put(bo->tbo.moving);
bo->tbo.moving = dma_fence_get(fence);
dma_resv_add_fence(bo->tbo.base.resv, fence,
DMA_RESV_USAGE_KERNEL);
dma_fence_put(fence);
}
if (!bp->resv)
@@ -761,6 +760,11 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)
if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
return -EPERM;
r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL,
false, MAX_SCHEDULE_TIMEOUT);
if (r < 0)
return r;
kptr = amdgpu_bo_kptr(bo);
if (kptr) {
if (ptr)
@@ -768,11 +772,6 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)
return 0;
}
r = dma_resv_wait_timeout(bo->tbo.base.resv, false, false,
MAX_SCHEDULE_TIMEOUT);
if (r < 0)
return r;
r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.resource->num_pages, &bo->kmap);
if (r)
return r;
@@ -1399,10 +1398,8 @@ void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,
return;
}
if (shared)
dma_resv_add_shared_fence(resv, fence);
else
dma_resv_add_excl_fence(resv, fence);
dma_resv_add_fence(resv, fence, shared ? DMA_RESV_USAGE_READ :
DMA_RESV_USAGE_WRITE);
}
/**
+78 -19
View File
@@ -30,12 +30,15 @@
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_range_manager.h>
#include "amdgpu_vram_mgr.h"
/* state back for walking over vram_mgr and gtt_mgr allocations */
struct amdgpu_res_cursor {
uint64_t start;
uint64_t size;
uint64_t remaining;
struct drm_mm_node *node;
void *node;
uint32_t mem_type;
};
/**
@@ -52,27 +55,63 @@ static inline void amdgpu_res_first(struct ttm_resource *res,
uint64_t start, uint64_t size,
struct amdgpu_res_cursor *cur)
{
struct drm_buddy_block *block;
struct list_head *head, *next;
struct drm_mm_node *node;
if (!res || res->mem_type == TTM_PL_SYSTEM) {
cur->start = start;
cur->size = size;
cur->remaining = size;
cur->node = NULL;
WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT);
return;
}
if (!res)
goto fallback;
BUG_ON(start + size > res->num_pages << PAGE_SHIFT);
node = to_ttm_range_mgr_node(res)->mm_nodes;
while (start >= node->size << PAGE_SHIFT)
start -= node++->size << PAGE_SHIFT;
cur->mem_type = res->mem_type;
cur->start = (node->start << PAGE_SHIFT) + start;
cur->size = min((node->size << PAGE_SHIFT) - start, size);
switch (cur->mem_type) {
case TTM_PL_VRAM:
head = &to_amdgpu_vram_mgr_resource(res)->blocks;
block = list_first_entry_or_null(head,
struct drm_buddy_block,
link);
if (!block)
goto fallback;
while (start >= amdgpu_vram_mgr_block_size(block)) {
start -= amdgpu_vram_mgr_block_size(block);
next = block->link.next;
if (next != head)
block = list_entry(next, struct drm_buddy_block, link);
}
cur->start = amdgpu_vram_mgr_block_start(block) + start;
cur->size = min(amdgpu_vram_mgr_block_size(block) - start, size);
cur->remaining = size;
cur->node = block;
break;
case TTM_PL_TT:
node = to_ttm_range_mgr_node(res)->mm_nodes;
while (start >= node->size << PAGE_SHIFT)
start -= node++->size << PAGE_SHIFT;
cur->start = (node->start << PAGE_SHIFT) + start;
cur->size = min((node->size << PAGE_SHIFT) - start, size);
cur->remaining = size;
cur->node = node;
break;
default:
goto fallback;
}
return;
fallback:
cur->start = start;
cur->size = size;
cur->remaining = size;
cur->node = node;
cur->node = NULL;
WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT);
return;
}
/**
@@ -85,7 +124,9 @@ static inline void amdgpu_res_first(struct ttm_resource *res,
*/
static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)
{
struct drm_mm_node *node = cur->node;
struct drm_buddy_block *block;
struct drm_mm_node *node;
struct list_head *next;
BUG_ON(size > cur->remaining);
@@ -99,9 +140,27 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)
return;
}
cur->node = ++node;
cur->start = node->start << PAGE_SHIFT;
cur->size = min(node->size << PAGE_SHIFT, cur->remaining);
switch (cur->mem_type) {
case TTM_PL_VRAM:
block = cur->node;
next = block->link.next;
block = list_entry(next, struct drm_buddy_block, link);
cur->node = block;
cur->start = amdgpu_vram_mgr_block_start(block);
cur->size = min(amdgpu_vram_mgr_block_size(block), cur->remaining);
break;
case TTM_PL_TT:
node = cur->node;
cur->node = ++node;
cur->start = node->start << PAGE_SHIFT;
cur->size = min(node->size << PAGE_SHIFT, cur->remaining);
break;
default:
return;
}
}
#endif
+2 -1
View File
@@ -259,7 +259,8 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync,
if (resv == NULL)
return -EINVAL;
dma_resv_for_each_fence(&cursor, resv, true, f) {
/* TODO: Use DMA_RESV_USAGE_READ here */
dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, f) {
dma_fence_chain_for_each(f, f) {
struct dma_fence *tmp = dma_fence_chain_contained(f);
+18 -71
View File
@@ -1344,7 +1344,8 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
* If true, then return false as any KFD process needs all its BOs to
* be resident to run successfully
*/
dma_resv_for_each_fence(&resv_cursor, bo->base.resv, true, f) {
dma_resv_for_each_fence(&resv_cursor, bo->base.resv,
DMA_RESV_USAGE_BOOKKEEP, f) {
if (amdkfd_fence_check_mm(f, current->mm))
return false;
}
@@ -2160,17 +2161,6 @@ int amdgpu_ttm_evict_resources(struct amdgpu_device *adev, int mem_type)
#if defined(CONFIG_DEBUG_FS)
static int amdgpu_mm_vram_table_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
TTM_PL_VRAM);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
@@ -2178,55 +2168,6 @@ static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)
return ttm_pool_debugfs(&adev->mman.bdev.pool, m);
}
static int amdgpu_mm_tt_table_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
TTM_PL_TT);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
static int amdgpu_mm_gds_table_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_GDS);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
static int amdgpu_mm_gws_table_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_GWS);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
static int amdgpu_mm_oa_table_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_OA);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_vram_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_tt_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gds_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gws_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_oa_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_ttm_page_pool);
/*
@@ -2436,17 +2377,23 @@ void amdgpu_ttm_debugfs_init(struct amdgpu_device *adev)
&amdgpu_ttm_vram_fops, adev->gmc.mc_vram_size);
debugfs_create_file("amdgpu_iomem", 0444, root, adev,
&amdgpu_ttm_iomem_fops);
debugfs_create_file("amdgpu_vram_mm", 0444, root, adev,
&amdgpu_mm_vram_table_fops);
debugfs_create_file("amdgpu_gtt_mm", 0444, root, adev,
&amdgpu_mm_tt_table_fops);
debugfs_create_file("amdgpu_gds_mm", 0444, root, adev,
&amdgpu_mm_gds_table_fops);
debugfs_create_file("amdgpu_gws_mm", 0444, root, adev,
&amdgpu_mm_gws_table_fops);
debugfs_create_file("amdgpu_oa_mm", 0444, root, adev,
&amdgpu_mm_oa_table_fops);
debugfs_create_file("ttm_page_pool", 0444, root, adev,
&amdgpu_ttm_page_pool_fops);
ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
TTM_PL_VRAM),
root, "amdgpu_vram_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
TTM_PL_TT),
root, "amdgpu_gtt_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_GDS),
root, "amdgpu_gds_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_GWS),
root, "amdgpu_gws_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
AMDGPU_PL_OA),
root, "amdgpu_oa_mm");
#endif
}
+1 -9
View File
@@ -26,6 +26,7 @@
#include <linux/dma-direction.h>
#include <drm/gpu_scheduler.h>
#include "amdgpu_vram_mgr.h"
#include "amdgpu.h"
#define AMDGPU_PL_GDS (TTM_PL_PRIV + 0)
@@ -38,15 +39,6 @@
#define AMDGPU_POISON 0xd0bed0be
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager;
struct drm_mm mm;
spinlock_t lock;
struct list_head reservations_pending;
struct list_head reserved_pages;
atomic64_t vis_usage;
};
struct amdgpu_gtt_mgr {
struct ttm_resource_manager manager;
struct drm_mm mm;
+2 -1
View File
@@ -1163,7 +1163,8 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo,
ib->length_dw = 16;
if (direct) {
r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
r = dma_resv_wait_timeout(bo->tbo.base.resv,
DMA_RESV_USAGE_KERNEL, false,
msecs_to_jiffies(10));
if (r == 0)
r = -ETIMEDOUT;
+4 -3
View File
@@ -2059,7 +2059,7 @@ static void amdgpu_vm_prt_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_for_each_fence(&cursor, resv, true, fence) {
dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, fence) {
/* Add a callback for each fence in the reservation object */
amdgpu_vm_prt_get(adev);
amdgpu_vm_add_prt_cb(adev, fence);
@@ -2665,7 +2665,7 @@ bool amdgpu_vm_evictable(struct amdgpu_bo *bo)
return true;
/* Don't evict VM page tables while they are busy */
if (!dma_resv_test_signaled(bo->tbo.base.resv, true))
if (!dma_resv_test_signaled(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP))
return false;
/* Try to block ongoing updates */
@@ -2845,7 +2845,8 @@ void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,
*/
long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout)
{
timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv, true,
timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP,
true, timeout);
if (timeout <= 0)
return timeout;
+5 -6
View File
@@ -74,13 +74,12 @@ static int amdgpu_vm_cpu_update(struct amdgpu_vm_update_params *p,
{
unsigned int i;
uint64_t value;
int r;
long r;
if (vmbo->bo.tbo.moving) {
r = dma_fence_wait(vmbo->bo.tbo.moving, true);
if (r)
return r;
}
r = dma_resv_wait_timeout(vmbo->bo.tbo.base.resv, DMA_RESV_USAGE_KERNEL,
true, MAX_SCHEDULE_TIMEOUT);
if (r < 0)
return r;
pe += (unsigned long)amdgpu_bo_kptr(&vmbo->bo);
+8 -3
View File
@@ -204,14 +204,19 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p,
struct amdgpu_bo *bo = &vmbo->bo;
enum amdgpu_ib_pool_type pool = p->immediate ? AMDGPU_IB_POOL_IMMEDIATE
: AMDGPU_IB_POOL_DELAYED;
struct dma_resv_iter cursor;
unsigned int i, ndw, nptes;
struct dma_fence *fence;
uint64_t *pte;
int r;
/* Wait for PD/PT moves to be completed */
r = amdgpu_sync_fence(&p->job->sync, bo->tbo.moving);
if (r)
return r;
dma_resv_for_each_fence(&cursor, bo->tbo.base.resv,
DMA_RESV_USAGE_KERNEL, fence) {
r = amdgpu_sync_fence(&p->job->sync, fence);
if (r)
return r;
}
do {
ndw = p->num_dw_left;
+206 -143
View File
@@ -32,8 +32,10 @@
#include "atom.h"
struct amdgpu_vram_reservation {
struct list_head node;
struct drm_mm_node mm_node;
u64 start;
u64 size;
struct list_head allocated;
struct list_head blocks;
};
static inline struct amdgpu_vram_mgr *
@@ -186,18 +188,18 @@ const struct attribute_group amdgpu_vram_mgr_attr_group = {
};
/**
* amdgpu_vram_mgr_vis_size - Calculate visible node size
* amdgpu_vram_mgr_vis_size - Calculate visible block size
*
* @adev: amdgpu_device pointer
* @node: MM node structure
* @block: DRM BUDDY block structure
*
* Calculate how many bytes of the MM node are inside visible VRAM
* Calculate how many bytes of the DRM BUDDY block are inside visible VRAM
*/
static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
struct drm_mm_node *node)
struct drm_buddy_block *block)
{
uint64_t start = node->start << PAGE_SHIFT;
uint64_t end = (node->size + node->start) << PAGE_SHIFT;
u64 start = amdgpu_vram_mgr_block_start(block);
u64 end = start + amdgpu_vram_mgr_block_size(block);
if (start >= adev->gmc.visible_vram_size)
return 0;
@@ -218,9 +220,9 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
struct ttm_resource *res = bo->tbo.resource;
unsigned pages = res->num_pages;
struct drm_mm_node *mm;
u64 usage;
struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);
struct drm_buddy_block *block;
u64 usage = 0;
if (amdgpu_gmc_vram_full_visible(&adev->gmc))
return amdgpu_bo_size(bo);
@@ -228,9 +230,8 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
if (res->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT)
return 0;
mm = &container_of(res, struct ttm_range_mgr_node, base)->mm_nodes[0];
for (usage = 0; pages; pages -= mm->size, mm++)
usage += amdgpu_vram_mgr_vis_size(adev, mm);
list_for_each_entry(block, &vres->blocks, link)
usage += amdgpu_vram_mgr_vis_size(adev, block);
return usage;
}
@@ -240,23 +241,30 @@ static void amdgpu_vram_mgr_do_reserve(struct ttm_resource_manager *man)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_mm *mm = &mgr->mm;
struct drm_buddy *mm = &mgr->mm;
struct amdgpu_vram_reservation *rsv, *temp;
struct drm_buddy_block *block;
uint64_t vis_usage;
list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node) {
if (drm_mm_reserve_node(mm, &rsv->mm_node))
list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks) {
if (drm_buddy_alloc_blocks(mm, rsv->start, rsv->start + rsv->size,
rsv->size, mm->chunk_size, &rsv->allocated,
DRM_BUDDY_RANGE_ALLOCATION))
continue;
block = amdgpu_vram_mgr_first_block(&rsv->allocated);
if (!block)
continue;
dev_dbg(adev->dev, "Reservation 0x%llx - %lld, Succeeded\n",
rsv->mm_node.start, rsv->mm_node.size);
rsv->start, rsv->size);
vis_usage = amdgpu_vram_mgr_vis_size(adev, &rsv->mm_node);
vis_usage = amdgpu_vram_mgr_vis_size(adev, block);
atomic64_add(vis_usage, &mgr->vis_usage);
spin_lock(&man->bdev->lru_lock);
man->usage += rsv->mm_node.size << PAGE_SHIFT;
man->usage += rsv->size;
spin_unlock(&man->bdev->lru_lock);
list_move(&rsv->node, &mgr->reserved_pages);
list_move(&rsv->blocks, &mgr->reserved_pages);
}
}
@@ -278,14 +286,16 @@ int amdgpu_vram_mgr_reserve_range(struct amdgpu_vram_mgr *mgr,
if (!rsv)
return -ENOMEM;
INIT_LIST_HEAD(&rsv->node);
rsv->mm_node.start = start >> PAGE_SHIFT;
rsv->mm_node.size = size >> PAGE_SHIFT;
INIT_LIST_HEAD(&rsv->allocated);
INIT_LIST_HEAD(&rsv->blocks);
spin_lock(&mgr->lock);
list_add_tail(&rsv->node, &mgr->reservations_pending);
rsv->start = start;
rsv->size = size;
mutex_lock(&mgr->lock);
list_add_tail(&rsv->blocks, &mgr->reservations_pending);
amdgpu_vram_mgr_do_reserve(&mgr->manager);
spin_unlock(&mgr->lock);
mutex_unlock(&mgr->lock);
return 0;
}
@@ -307,19 +317,19 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,
struct amdgpu_vram_reservation *rsv;
int ret;
spin_lock(&mgr->lock);
mutex_lock(&mgr->lock);
list_for_each_entry(rsv, &mgr->reservations_pending, node) {
if ((rsv->mm_node.start <= start) &&
(start < (rsv->mm_node.start + rsv->mm_node.size))) {
list_for_each_entry(rsv, &mgr->reservations_pending, blocks) {
if (rsv->start <= start &&
(start < (rsv->start + rsv->size))) {
ret = -EBUSY;
goto out;
}
}
list_for_each_entry(rsv, &mgr->reserved_pages, node) {
if ((rsv->mm_node.start <= start) &&
(start < (rsv->mm_node.start + rsv->mm_node.size))) {
list_for_each_entry(rsv, &mgr->reserved_pages, blocks) {
if (rsv->start <= start &&
(start < (rsv->start + rsv->size))) {
ret = 0;
goto out;
}
@@ -327,32 +337,10 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,
ret = -ENOENT;
out:
spin_unlock(&mgr->lock);
mutex_unlock(&mgr->lock);
return ret;
}
/**
* amdgpu_vram_mgr_virt_start - update virtual start address
*
* @mem: ttm_resource to update
* @node: just allocated node
*
* Calculate a virtual BO start address to easily check if everything is CPU
* accessible.
*/
static void amdgpu_vram_mgr_virt_start(struct ttm_resource *mem,
struct drm_mm_node *node)
{
unsigned long start;
start = node->start + node->size;
if (start > mem->num_pages)
start -= mem->num_pages;
else
start = 0;
mem->start = max(mem->start, start);
}
/**
* amdgpu_vram_mgr_new - allocate new ranges
*
@@ -368,46 +356,44 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
const struct ttm_place *place,
struct ttm_resource **res)
{
unsigned long lpfn, num_nodes, pages_per_node, pages_left, pages;
u64 vis_usage = 0, max_bytes, cur_size, min_block_size;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
uint64_t vis_usage = 0, mem_bytes, max_bytes;
struct ttm_range_mgr_node *node;
struct drm_mm *mm = &mgr->mm;
enum drm_mm_insert_mode mode;
unsigned i;
struct amdgpu_vram_mgr_resource *vres;
u64 size, remaining_size, lpfn, fpfn;
struct drm_buddy *mm = &mgr->mm;
struct drm_buddy_block *block;
unsigned long pages_per_block;
int r;
lpfn = place->lpfn;
lpfn = place->lpfn << PAGE_SHIFT;
if (!lpfn)
lpfn = man->size >> PAGE_SHIFT;
lpfn = man->size;
fpfn = place->fpfn << PAGE_SHIFT;
max_bytes = adev->gmc.mc_vram_size;
if (tbo->type != ttm_bo_type_kernel)
max_bytes -= AMDGPU_VM_RESERVED_VRAM;
mem_bytes = tbo->base.size;
if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
pages_per_node = ~0ul;
num_nodes = 1;
pages_per_block = ~0ul;
} else {
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
pages_per_node = HPAGE_PMD_NR;
pages_per_block = HPAGE_PMD_NR;
#else
/* default to 2MB */
pages_per_node = 2UL << (20UL - PAGE_SHIFT);
pages_per_block = 2UL << (20UL - PAGE_SHIFT);
#endif
pages_per_node = max_t(uint32_t, pages_per_node,
tbo->page_alignment);
num_nodes = DIV_ROUND_UP_ULL(PFN_UP(mem_bytes), pages_per_node);
pages_per_block = max_t(uint32_t, pages_per_block,
tbo->page_alignment);
}
node = kvmalloc(struct_size(node, mm_nodes, num_nodes),
GFP_KERNEL | __GFP_ZERO);
if (!node)
vres = kzalloc(sizeof(*vres), GFP_KERNEL);
if (!vres)
return -ENOMEM;
ttm_resource_init(tbo, place, &node->base);
ttm_resource_init(tbo, place, &vres->base);
/* bail out quickly if there's likely not enough VRAM for this BO */
if (ttm_resource_manager_usage(man) > max_bytes) {
@@ -415,66 +401,130 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
goto error_fini;
}
mode = DRM_MM_INSERT_BEST;
INIT_LIST_HEAD(&vres->blocks);
if (place->flags & TTM_PL_FLAG_TOPDOWN)
mode = DRM_MM_INSERT_HIGH;
vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;
pages_left = node->base.num_pages;
if (fpfn || lpfn != man->size)
/* Allocate blocks in desired range */
vres->flags |= DRM_BUDDY_RANGE_ALLOCATION;
/* Limit maximum size to 2GB due to SG table limitations */
pages = min(pages_left, 2UL << (30 - PAGE_SHIFT));
remaining_size = vres->base.num_pages << PAGE_SHIFT;
i = 0;
spin_lock(&mgr->lock);
while (pages_left) {
uint32_t alignment = tbo->page_alignment;
mutex_lock(&mgr->lock);
while (remaining_size) {
if (tbo->page_alignment)
min_block_size = tbo->page_alignment << PAGE_SHIFT;
else
min_block_size = mgr->default_page_size;
if (pages >= pages_per_node)
alignment = pages_per_node;
BUG_ON(min_block_size < mm->chunk_size);
r = drm_mm_insert_node_in_range(mm, &node->mm_nodes[i], pages,
alignment, 0, place->fpfn,
lpfn, mode);
if (unlikely(r)) {
if (pages > pages_per_node) {
if (is_power_of_2(pages))
pages = pages / 2;
else
pages = rounddown_pow_of_two(pages);
continue;
/* Limit maximum size to 2GiB due to SG table limitations */
size = min(remaining_size, 2ULL << 30);
if (size >= pages_per_block << PAGE_SHIFT)
min_block_size = pages_per_block << PAGE_SHIFT;
cur_size = size;
if (fpfn + size != place->lpfn << PAGE_SHIFT) {
/*
* Except for actual range allocation, modify the size and
* min_block_size conforming to continuous flag enablement
*/
if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
size = roundup_pow_of_two(size);
min_block_size = size;
/*
* Modify the size value if size is not
* aligned with min_block_size
*/
} else if (!IS_ALIGNED(size, min_block_size)) {
size = round_up(size, min_block_size);
}
goto error_free;
}
vis_usage += amdgpu_vram_mgr_vis_size(adev, &node->mm_nodes[i]);
amdgpu_vram_mgr_virt_start(&node->base, &node->mm_nodes[i]);
pages_left -= pages;
++i;
r = drm_buddy_alloc_blocks(mm, fpfn,
lpfn,
size,
min_block_size,
&vres->blocks,
vres->flags);
if (unlikely(r))
goto error_free_blocks;
if (pages > pages_left)
pages = pages_left;
if (size > remaining_size)
remaining_size = 0;
else
remaining_size -= size;
}
spin_unlock(&mgr->lock);
mutex_unlock(&mgr->lock);
if (i == 1)
node->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
if (cur_size != size) {
struct drm_buddy_block *block;
struct list_head *trim_list;
u64 original_size;
LIST_HEAD(temp);
trim_list = &vres->blocks;
original_size = vres->base.num_pages << PAGE_SHIFT;
/*
* If size value is rounded up to min_block_size, trim the last
* block to the required size
*/
if (!list_is_singular(&vres->blocks)) {
block = list_last_entry(&vres->blocks, typeof(*block), link);
list_move_tail(&block->link, &temp);
trim_list = &temp;
/*
* Compute the original_size value by subtracting the
* last block size with (aligned size - original size)
*/
original_size = amdgpu_vram_mgr_block_size(block) - (size - cur_size);
}
mutex_lock(&mgr->lock);
drm_buddy_block_trim(mm,
original_size,
trim_list);
mutex_unlock(&mgr->lock);
if (!list_empty(&temp))
list_splice_tail(trim_list, &vres->blocks);
}
list_for_each_entry(block, &vres->blocks, link)
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;
if (adev->gmc.xgmi.connected_to_cpu)
node->base.bus.caching = ttm_cached;
vres->base.bus.caching = ttm_cached;
else
node->base.bus.caching = ttm_write_combined;
vres->base.bus.caching = ttm_write_combined;
atomic64_add(vis_usage, &mgr->vis_usage);
*res = &node->base;
*res = &vres->base;
return 0;
error_free:
while (i--)
drm_mm_remove_node(&node->mm_nodes[i]);
spin_unlock(&mgr->lock);
error_free_blocks:
drm_buddy_free_list(mm, &vres->blocks);
mutex_unlock(&mgr->lock);
error_fini:
ttm_resource_fini(man, &node->base);
kvfree(node);
ttm_resource_fini(man, &vres->base);
kfree(vres);
return r;
}
@@ -490,27 +540,26 @@ error_fini:
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);
struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_buddy *mm = &mgr->mm;
struct drm_buddy_block *block;
uint64_t vis_usage = 0;
unsigned i, pages;
spin_lock(&mgr->lock);
for (i = 0, pages = res->num_pages; pages;
pages -= node->mm_nodes[i].size, ++i) {
struct drm_mm_node *mm = &node->mm_nodes[i];
mutex_lock(&mgr->lock);
list_for_each_entry(block, &vres->blocks, link)
vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
drm_mm_remove_node(mm);
vis_usage += amdgpu_vram_mgr_vis_size(adev, mm);
}
amdgpu_vram_mgr_do_reserve(man);
spin_unlock(&mgr->lock);
drm_buddy_free_list(mm, &vres->blocks);
mutex_unlock(&mgr->lock);
atomic64_sub(vis_usage, &mgr->vis_usage);
ttm_resource_fini(man, res);
kvfree(node);
kfree(vres);
}
/**
@@ -542,7 +591,7 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
if (!*sgt)
return -ENOMEM;
/* Determine the number of DRM_MM nodes to export */
/* Determine the number of DRM_BUDDY blocks to export */
amdgpu_res_first(res, offset, length, &cursor);
while (cursor.remaining) {
num_entries++;
@@ -558,10 +607,10 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
sg->length = 0;
/*
* Walk down DRM_MM nodes to populate scatterlist nodes
* @note: Use iterator api to get first the DRM_MM node
* Walk down DRM_BUDDY blocks to populate scatterlist nodes
* @note: Use iterator api to get first the DRM_BUDDY block
* and the number of bytes from it. Access the following
* DRM_MM node(s) if more buffer needs to exported
* DRM_BUDDY block(s) if more buffer needs to exported
*/
amdgpu_res_first(res, offset, length, &cursor);
for_each_sgtable_sg((*sgt), sg, i) {
@@ -648,13 +697,22 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct drm_buddy *mm = &mgr->mm;
struct drm_buddy_block *block;
drm_printf(printer, " vis usage:%llu\n",
amdgpu_vram_mgr_vis_usage(mgr));
spin_lock(&mgr->lock);
drm_mm_print(&mgr->mm, printer);
spin_unlock(&mgr->lock);
mutex_lock(&mgr->lock);
drm_printf(printer, "default_page_size: %lluKiB\n",
mgr->default_page_size >> 10);
drm_buddy_print(mm, printer);
drm_printf(printer, "reserved:\n");
list_for_each_entry(block, &mgr->reserved_pages, link)
drm_buddy_block_print(mm, block, printer);
mutex_unlock(&mgr->lock);
}
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
@@ -674,16 +732,21 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
int err;
ttm_resource_manager_init(man, &adev->mman.bdev,
adev->gmc.real_vram_size);
man->func = &amdgpu_vram_mgr_func;
drm_mm_init(&mgr->mm, 0, man->size >> PAGE_SHIFT);
spin_lock_init(&mgr->lock);
err = drm_buddy_init(&mgr->mm, man->size, PAGE_SIZE);
if (err)
return err;
mutex_init(&mgr->lock);
INIT_LIST_HEAD(&mgr->reservations_pending);
INIT_LIST_HEAD(&mgr->reserved_pages);
mgr->default_page_size = PAGE_SIZE;
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
ttm_resource_manager_set_used(man, true);
@@ -711,16 +774,16 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
if (ret)
return;
spin_lock(&mgr->lock);
list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node)
mutex_lock(&mgr->lock);
list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks)
kfree(rsv);
list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, node) {
drm_mm_remove_node(&rsv->mm_node);
list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) {
drm_buddy_free_list(&mgr->mm, &rsv->blocks);
kfree(rsv);
}
drm_mm_takedown(&mgr->mm);
spin_unlock(&mgr->lock);
drm_buddy_fini(&mgr->mm);
mutex_unlock(&mgr->lock);
ttm_resource_manager_cleanup(man);
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL);
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: MIT
* Copyright 2021 Advanced Micro Devices, 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, sublicense,
* 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 above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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 NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
*
*/
#ifndef __AMDGPU_VRAM_MGR_H__
#define __AMDGPU_VRAM_MGR_H__
#include <drm/drm_buddy.h>
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager;
struct drm_buddy mm;
/* protects access to buffer objects */
struct mutex lock;
struct list_head reservations_pending;
struct list_head reserved_pages;
atomic64_t vis_usage;
u64 default_page_size;
};
struct amdgpu_vram_mgr_resource {
struct ttm_resource base;
struct list_head blocks;
unsigned long flags;
};
static inline u64 amdgpu_vram_mgr_block_start(struct drm_buddy_block *block)
{
return drm_buddy_block_offset(block);
}
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)
{
return container_of(res, struct amdgpu_vram_mgr_resource, base);
}
#endif
@@ -9238,7 +9238,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
* 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, true, false,
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!");
@@ -256,6 +256,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,
formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
layer->layer_type, &n_formats);
if (!formats) {
kfree(kplane);
return -ENOMEM;
}
err = drm_universal_plane_init(&kms->base, plane,
get_possible_crtcs(kms, c->pipeline),
@@ -266,8 +270,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,
komeda_put_fourcc_list(formats);
if (err)
goto cleanup;
if (err) {
kfree(kplane);
return err;
}
drm_plane_helper_add(plane, &komeda_plane_helper_funcs);
+4 -1
View File
@@ -487,7 +487,10 @@ static void malidp_crtc_reset(struct drm_crtc *crtc)
if (crtc->state)
malidp_crtc_destroy_state(crtc, crtc->state);
__drm_atomic_helper_crtc_reset(crtc, &state->base);
if (state)
__drm_atomic_helper_crtc_reset(crtc, &state->base);
else
__drm_atomic_helper_crtc_reset(crtc, NULL);
}
static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
+5 -9
View File
@@ -310,17 +310,13 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,
static u32 malidp_get_pgsize_bitmap(struct malidp_plane *mp)
{
u32 pgsize_bitmap = 0;
struct iommu_domain *mmu_dom;
if (iommu_present(&platform_bus_type)) {
struct iommu_domain *mmu_dom =
iommu_get_domain_for_dev(mp->base.dev->dev);
mmu_dom = iommu_get_domain_for_dev(mp->base.dev->dev);
if (mmu_dom)
return mmu_dom->pgsize_bitmap;
if (mmu_dom)
pgsize_bitmap = mmu_dom->pgsize_bitmap;
}
return pgsize_bitmap;
return 0;
}
/*
+14
View File
@@ -32,6 +32,7 @@ config DRM_CHIPONE_ICN6211
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
select REGMAP_I2C
help
ICN6211 is MIPI-DSI/RGB Converter bridge from chipone.
@@ -99,6 +100,19 @@ config DRM_LONTIUM_LT8912B
Say M here if you want to support this hardware as a module.
The module will be named "lontium-lt8912b".
config DRM_LONTIUM_LT9211
tristate "Lontium LT9211 DSI/LVDS/DPI bridge"
depends on OF
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select REGMAP_I2C
help
Driver for Lontium LT9211 Single/Dual-Link DSI/LVDS or Single DPI
input to Single-link/Dual-Link DSI/LVDS or Single DPI output bridge
chip.
Please say Y if you have such hardware.
config DRM_LONTIUM_LT9611
tristate "Lontium LT9611 DSI/HDMI bridge"
select SND_SOC_HDMI_CODEC if SND_SOC
+1
View File
@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
+4 -2
View File
@@ -1292,8 +1292,10 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_unregister_cec;
adv7511->bridge.funcs = &adv7511_bridge_funcs;
adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_HPD;
adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
if (adv7511->i2c_main->irq)
adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD;
adv7511->bridge.of_node = dev->of_node;
adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+12 -12
View File
@@ -1486,12 +1486,12 @@ static void anx7625_dp_adjust_swing(struct anx7625_data *ctx)
for (i = 0; i < ctx->pdata.dp_lane0_swing_reg_cnt; i++)
anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,
DP_TX_LANE0_SWING_REG0 + i,
ctx->pdata.lane0_reg_data[i] & 0xFF);
ctx->pdata.lane0_reg_data[i]);
for (i = 0; i < ctx->pdata.dp_lane1_swing_reg_cnt; i++)
anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,
DP_TX_LANE1_SWING_REG0 + i,
ctx->pdata.lane1_reg_data[i] & 0xFF);
ctx->pdata.lane1_reg_data[i]);
}
static void dp_hpd_change_handler(struct anx7625_data *ctx, bool on)
@@ -1598,8 +1598,8 @@ static int anx7625_get_swing_setting(struct device *dev,
num_regs = DP_TX_SWING_REG_CNT;
pdata->dp_lane0_swing_reg_cnt = num_regs;
of_property_read_u32_array(dev->of_node, "analogix,lane0-swing",
pdata->lane0_reg_data, num_regs);
of_property_read_u8_array(dev->of_node, "analogix,lane0-swing",
pdata->lane0_reg_data, num_regs);
}
if (of_get_property(dev->of_node,
@@ -1608,8 +1608,8 @@ static int anx7625_get_swing_setting(struct device *dev,
num_regs = DP_TX_SWING_REG_CNT;
pdata->dp_lane1_swing_reg_cnt = num_regs;
of_property_read_u32_array(dev->of_node, "analogix,lane1-swing",
pdata->lane1_reg_data, num_regs);
of_property_read_u8_array(dev->of_node, "analogix,lane1-swing",
pdata->lane1_reg_data, num_regs);
}
return 0;
@@ -1932,14 +1932,14 @@ static int anx7625_audio_get_eld(struct device *dev, void *data,
struct anx7625_data *ctx = dev_get_drvdata(dev);
if (!ctx->connector) {
dev_err(dev, "connector not initial\n");
return -EINVAL;
/* Pass en empty ELD if connector not available */
memset(buf, 0, len);
} else {
dev_dbg(dev, "audio copy eld\n");
memcpy(buf, ctx->connector->eld,
min(sizeof(ctx->connector->eld), len));
}
dev_dbg(dev, "audio copy eld\n");
memcpy(buf, ctx->connector->eld,
min(sizeof(ctx->connector->eld), len));
return 0;
}
+2 -2
View File
@@ -426,9 +426,9 @@ struct anx7625_platform_data {
int mipi_lanes;
int audio_en;
int dp_lane0_swing_reg_cnt;
int lane0_reg_data[DP_TX_SWING_REG_CNT];
u8 lane0_reg_data[DP_TX_SWING_REG_CNT];
int dp_lane1_swing_reg_cnt;
int lane1_reg_data[DP_TX_SWING_REG_CNT];
u8 lane1_reg_data[DP_TX_SWING_REG_CNT];
u32 low_power_mode;
struct device_node *mipi_host_node;
};
+109 -10
View File
@@ -14,6 +14,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#define VENDOR_ID 0x00
@@ -134,6 +135,7 @@
struct chipone {
struct device *dev;
struct regmap *regmap;
struct i2c_client *client;
struct drm_bridge bridge;
struct drm_display_mode mode;
@@ -146,6 +148,77 @@ struct chipone {
bool interface_i2c;
};
static const struct regmap_range chipone_dsi_readable_ranges[] = {
regmap_reg_range(VENDOR_ID, VERSION_ID),
regmap_reg_range(FIRMWARE_VERSION, PLL_SSC_OFFSET(3)),
regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL),
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_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)),
};
static const struct regmap_access_table chipone_dsi_readable_table = {
.yes_ranges = chipone_dsi_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(chipone_dsi_readable_ranges),
};
static const struct regmap_range chipone_dsi_writeable_ranges[] = {
regmap_reg_range(CONFIG_FINISH, PLL_SSC_OFFSET(3)),
regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL),
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_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)),
};
static const struct regmap_access_table chipone_dsi_writeable_table = {
.yes_ranges = chipone_dsi_writeable_ranges,
.n_yes_ranges = ARRAY_SIZE(chipone_dsi_writeable_ranges),
};
static const struct regmap_config chipone_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &chipone_dsi_readable_table,
.wr_table = &chipone_dsi_writeable_table,
.cache_type = REGCACHE_RBTREE,
.max_register = MIPI_ATE_STATUS_(1),
};
static int chipone_dsi_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct mipi_dsi_device *dsi = context;
const u16 reg16 = (val_size << 8) | *(u8 *)reg;
int ret;
ret = mipi_dsi_generic_read(dsi, &reg16, 2, val, val_size);
return ret == val_size ? 0 : -EINVAL;
}
static int chipone_dsi_write(void *context, const void *data, size_t count)
{
struct mipi_dsi_device *dsi = context;
return mipi_dsi_generic_write(dsi, data, 2);
}
static const struct regmap_bus chipone_dsi_regmap_bus = {
.read = chipone_dsi_read,
.write = chipone_dsi_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
{
return container_of(bridge, struct chipone, bridge);
@@ -153,18 +226,16 @@ static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
static void chipone_readb(struct chipone *icn, u8 reg, u8 *val)
{
if (icn->interface_i2c)
*val = i2c_smbus_read_byte_data(icn->client, reg);
else
mipi_dsi_generic_read(icn->dsi, (u8[]){reg, 1}, 2, val, 1);
int ret, pval;
ret = regmap_read(icn->regmap, reg, &pval);
*val = ret ? 0 : pval & 0xff;
}
static int chipone_writeb(struct chipone *icn, u8 reg, u8 val)
{
if (icn->interface_i2c)
return i2c_smbus_write_byte_data(icn->client, reg, val);
else
return mipi_dsi_generic_write(icn->dsi, (u8[]){reg, val}, 2);
return regmap_write(icn->regmap, reg, val);
}
static void chipone_configure_pll(struct chipone *icn,
@@ -324,6 +395,11 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
/* dsi specific sequence */
chipone_writeb(icn, SYNC_EVENT_DLY, 0x80);
chipone_writeb(icn, HFP_MIN, hfp & 0xff);
/* DSI data lane count */
chipone_writeb(icn, DSI_CTRL,
DSI_CTRL_UNKNOWN | DSI_CTRL_DSI_LANES(icn->dsi->lanes - 1));
chipone_writeb(icn, MIPI_PD_CK_LANE, 0xa0);
chipone_writeb(icn, PLL_CTRL(12), 0xff);
chipone_writeb(icn, MIPI_PN_SWAP, 0x00);
@@ -409,9 +485,23 @@ static void chipone_mode_set(struct drm_bridge *bridge,
static int chipone_dsi_attach(struct chipone *icn)
{
struct mipi_dsi_device *dsi = icn->dsi;
int ret;
struct device *dev = icn->dev;
struct device_node *endpoint;
int dsi_lanes, ret;
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
of_node_put(endpoint);
/*
* If the 'data-lanes' property does not exist in DT or is invalid,
* default to previously hard-coded behavior, which was 4 data lanes.
*/
if (dsi_lanes >= 1 && dsi_lanes <= 4)
icn->dsi->lanes = dsi_lanes;
else
icn->dsi->lanes = 4;
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
@@ -591,6 +681,11 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
icn->regmap = devm_regmap_init(dev, &chipone_dsi_regmap_bus,
dsi, &chipone_regmap_config);
if (IS_ERR(icn->regmap))
return PTR_ERR(icn->regmap);
icn->interface_i2c = false;
icn->dsi = dsi;
@@ -616,6 +711,10 @@ static int chipone_i2c_probe(struct i2c_client *client,
if (ret)
return ret;
icn->regmap = devm_regmap_init_i2c(client, &chipone_regmap_config);
if (IS_ERR(icn->regmap))
return PTR_ERR(icn->regmap);
icn->interface_i2c = true;
icn->client = client;
dev_set_drvdata(dev, icn);
@@ -24,6 +24,7 @@ struct display_connector {
int hpd_irq;
struct regulator *dp_pwr;
struct gpio_desc *ddc_en;
};
static inline struct display_connector *
@@ -345,6 +346,17 @@ static int display_connector_probe(struct platform_device *pdev)
}
}
/* enable DDC */
if (type == DRM_MODE_CONNECTOR_HDMIA) {
conn->ddc_en = devm_gpiod_get_optional(&pdev->dev, "ddc-en",
GPIOD_OUT_HIGH);
if (IS_ERR(conn->ddc_en)) {
dev_err(&pdev->dev, "Couldn't get ddc-en gpio\n");
return PTR_ERR(conn->ddc_en);
}
}
conn->bridge.funcs = &display_connector_bridge_funcs;
conn->bridge.of_node = pdev->dev.of_node;
@@ -373,6 +385,9 @@ static int display_connector_remove(struct platform_device *pdev)
{
struct display_connector *conn = platform_get_drvdata(pdev);
if (conn->ddc_en)
gpiod_set_value(conn->ddc_en, 0);
if (conn->dp_pwr)
regulator_disable(conn->dp_pwr);
+802
View File
@@ -0,0 +1,802 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Lontium LT9211 bridge driver
*
* LT9211 is capable of converting:
* 2xDSI/2xLVDS/1xDPI -> 2xDSI/2xLVDS/1xDPI
* Currently supported is:
* 1xDSI -> 1xLVDS
*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#define REG_PAGE_CONTROL 0xff
#define REG_CHIPID0 0x8100
#define REG_CHIPID0_VALUE 0x18
#define REG_CHIPID1 0x8101
#define REG_CHIPID1_VALUE 0x01
#define REG_CHIPID2 0x8102
#define REG_CHIPID2_VALUE 0xe3
#define REG_DSI_LANE 0xd000
/* DSI lane count - 0 means 4 lanes ; 1, 2, 3 means 1, 2, 3 lanes. */
#define REG_DSI_LANE_COUNT(n) ((n) & 3)
struct lt9211 {
struct drm_bridge bridge;
struct device *dev;
struct regmap *regmap;
struct mipi_dsi_device *dsi;
struct drm_bridge *panel_bridge;
struct gpio_desc *reset_gpio;
struct regulator *vccio;
bool lvds_dual_link;
bool lvds_dual_link_even_odd_swap;
};
static const struct regmap_range lt9211_rw_ranges[] = {
regmap_reg_range(0xff, 0xff),
regmap_reg_range(0x8100, 0x816b),
regmap_reg_range(0x8200, 0x82aa),
regmap_reg_range(0x8500, 0x85ff),
regmap_reg_range(0x8600, 0x86a0),
regmap_reg_range(0x8700, 0x8746),
regmap_reg_range(0xd000, 0xd0a7),
regmap_reg_range(0xd400, 0xd42c),
regmap_reg_range(0xd800, 0xd838),
regmap_reg_range(0xd9c0, 0xd9d5),
};
static const struct regmap_access_table lt9211_rw_table = {
.yes_ranges = lt9211_rw_ranges,
.n_yes_ranges = ARRAY_SIZE(lt9211_rw_ranges),
};
static const struct regmap_range_cfg lt9211_range = {
.name = "lt9211",
.range_min = 0x0000,
.range_max = 0xda00,
.selector_reg = REG_PAGE_CONTROL,
.selector_mask = 0xff,
.selector_shift = 0,
.window_start = 0,
.window_len = 0x100,
};
static const struct regmap_config lt9211_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &lt9211_rw_table,
.wr_table = &lt9211_rw_table,
.volatile_table = &lt9211_rw_table,
.ranges = &lt9211_range,
.num_ranges = 1,
.cache_type = REGCACHE_RBTREE,
.max_register = 0xda00,
};
static struct lt9211 *bridge_to_lt9211(struct drm_bridge *bridge)
{
return container_of(bridge, struct lt9211, bridge);
}
static int lt9211_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct lt9211 *ctx = bridge_to_lt9211(bridge);
return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
&ctx->bridge, flags);
}
static int lt9211_read_chipid(struct lt9211 *ctx)
{
u8 chipid[3];
int ret;
/* Read Chip ID registers and verify the chip can communicate. */
ret = regmap_bulk_read(ctx->regmap, REG_CHIPID0, chipid, 3);
if (ret < 0) {
dev_err(ctx->dev, "Failed to read Chip ID: %d\n", ret);
return ret;
}
/* Test for known Chip ID. */
if (chipid[0] != REG_CHIPID0_VALUE || chipid[1] != REG_CHIPID1_VALUE ||
chipid[2] != REG_CHIPID2_VALUE) {
dev_err(ctx->dev, "Unknown Chip ID: 0x%02x 0x%02x 0x%02x\n",
chipid[0], chipid[1], chipid[2]);
return -EINVAL;
}
return 0;
}
static int lt9211_system_init(struct lt9211 *ctx)
{
const struct reg_sequence lt9211_system_init_seq[] = {
{ 0x8201, 0x18 },
{ 0x8606, 0x61 },
{ 0x8607, 0xa8 },
{ 0x8714, 0x08 },
{ 0x8715, 0x00 },
{ 0x8718, 0x0f },
{ 0x8722, 0x08 },
{ 0x8723, 0x00 },
{ 0x8726, 0x0f },
{ 0x810b, 0xfe },
};
return regmap_multi_reg_write(ctx->regmap, lt9211_system_init_seq,
ARRAY_SIZE(lt9211_system_init_seq));
}
static int lt9211_configure_rx(struct lt9211 *ctx)
{
const struct reg_sequence lt9211_rx_phy_seq[] = {
{ 0x8202, 0x44 },
{ 0x8204, 0xa0 },
{ 0x8205, 0x22 },
{ 0x8207, 0x9f },
{ 0x8208, 0xfc },
/* ORR with 0xf8 here to enable DSI DN/DP swap. */
{ 0x8209, 0x01 },
{ 0x8217, 0x0c },
{ 0x8633, 0x1b },
};
const struct reg_sequence lt9211_rx_cal_reset_seq[] = {
{ 0x8120, 0x7f },
{ 0x8120, 0xff },
};
const struct reg_sequence lt9211_rx_dig_seq[] = {
{ 0x8630, 0x85 },
/* 0x8588: BIT 6 set = MIPI-RX, BIT 4 unset = LVDS-TX */
{ 0x8588, 0x40 },
{ 0x85ff, 0xd0 },
{ REG_DSI_LANE, REG_DSI_LANE_COUNT(ctx->dsi->lanes) },
{ 0xd002, 0x05 },
};
const struct reg_sequence lt9211_rx_div_reset_seq[] = {
{ 0x810a, 0xc0 },
{ 0x8120, 0xbf },
};
const struct reg_sequence lt9211_rx_div_clear_seq[] = {
{ 0x810a, 0xc1 },
{ 0x8120, 0xff },
};
int ret;
ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_phy_seq,
ARRAY_SIZE(lt9211_rx_phy_seq));
if (ret)
return ret;
ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_cal_reset_seq,
ARRAY_SIZE(lt9211_rx_cal_reset_seq));
if (ret)
return ret;
ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_dig_seq,
ARRAY_SIZE(lt9211_rx_dig_seq));
if (ret)
return ret;
ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_reset_seq,
ARRAY_SIZE(lt9211_rx_div_reset_seq));
if (ret)
return ret;
usleep_range(10000, 15000);
return regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_clear_seq,
ARRAY_SIZE(lt9211_rx_div_clear_seq));
}
static int lt9211_autodetect_rx(struct lt9211 *ctx,
const struct drm_display_mode *mode)
{
u16 width, height;
u32 byteclk;
u8 buf[5];
u8 format;
u8 bc[3];
int ret;
/* Measure ByteClock frequency. */
ret = regmap_write(ctx->regmap, 0x8600, 0x01);
if (ret)
return ret;
/* Give the chip time to lock onto RX stream. */
msleep(100);
/* Read the ByteClock frequency from the chip. */
ret = regmap_bulk_read(ctx->regmap, 0x8608, bc, sizeof(bc));
if (ret)
return ret;
/* RX ByteClock in kHz */
byteclk = ((bc[0] & 0xf) << 16) | (bc[1] << 8) | bc[2];
/* Width/Height/Format Auto-detection */
ret = regmap_bulk_read(ctx->regmap, 0xd082, buf, sizeof(buf));
if (ret)
return ret;
width = (buf[0] << 8) | buf[1];
height = (buf[3] << 8) | buf[4];
format = buf[2] & 0xf;
if (format == 0x3) { /* YUV422 16bit */
width /= 2;
} else if (format == 0xa) { /* RGB888 24bit */
width /= 3;
} else {
dev_err(ctx->dev, "Unsupported DSI pixel format 0x%01x\n",
format);
return -EINVAL;
}
if (width != mode->hdisplay) {
dev_err(ctx->dev,
"RX: Detected DSI width (%d) does not match mode hdisplay (%d)\n",
width, mode->hdisplay);
return -EINVAL;
}
if (height != mode->vdisplay) {
dev_err(ctx->dev,
"RX: Detected DSI height (%d) does not match mode vdisplay (%d)\n",
height, mode->vdisplay);
return -EINVAL;
}
dev_dbg(ctx->dev, "RX: %dx%d format=0x%01x byteclock=%d kHz\n",
width, height, format, byteclk);
return 0;
}
static int lt9211_configure_timing(struct lt9211 *ctx,
const struct drm_display_mode *mode)
{
const struct reg_sequence lt9211_timing[] = {
{ 0xd00d, (mode->vtotal >> 8) & 0xff },
{ 0xd00e, mode->vtotal & 0xff },
{ 0xd00f, (mode->vdisplay >> 8) & 0xff },
{ 0xd010, mode->vdisplay & 0xff },
{ 0xd011, (mode->htotal >> 8) & 0xff },
{ 0xd012, mode->htotal & 0xff },
{ 0xd013, (mode->hdisplay >> 8) & 0xff },
{ 0xd014, mode->hdisplay & 0xff },
{ 0xd015, (mode->vsync_end - mode->vsync_start) & 0xff },
{ 0xd016, (mode->hsync_end - mode->hsync_start) & 0xff },
{ 0xd017, ((mode->vsync_start - mode->vdisplay) >> 8) & 0xff },
{ 0xd018, (mode->vsync_start - mode->vdisplay) & 0xff },
{ 0xd019, ((mode->hsync_start - mode->hdisplay) >> 8) & 0xff },
{ 0xd01a, (mode->hsync_start - mode->hdisplay) & 0xff },
};
return regmap_multi_reg_write(ctx->regmap, lt9211_timing,
ARRAY_SIZE(lt9211_timing));
}
static int lt9211_configure_plls(struct lt9211 *ctx,
const struct drm_display_mode *mode)
{
const struct reg_sequence lt9211_pcr_seq[] = {
{ 0xd026, 0x17 },
{ 0xd027, 0xc3 },
{ 0xd02d, 0x30 },
{ 0xd031, 0x10 },
{ 0xd023, 0x20 },
{ 0xd038, 0x02 },
{ 0xd039, 0x10 },
{ 0xd03a, 0x20 },
{ 0xd03b, 0x60 },
{ 0xd03f, 0x04 },
{ 0xd040, 0x08 },
{ 0xd041, 0x10 },
{ 0x810b, 0xee },
{ 0x810b, 0xfe },
};
unsigned int pval;
int ret;
/* DeSSC PLL reference clock is 25 MHz XTal. */
ret = regmap_write(ctx->regmap, 0x822d, 0x48);
if (ret)
return ret;
if (mode->clock < 44000) {
ret = regmap_write(ctx->regmap, 0x8235, 0x83);
} else if (mode->clock < 88000) {
ret = regmap_write(ctx->regmap, 0x8235, 0x82);
} else if (mode->clock < 176000) {
ret = regmap_write(ctx->regmap, 0x8235, 0x81);
} else {
dev_err(ctx->dev,
"Unsupported mode clock (%d kHz) above 176 MHz.\n",
mode->clock);
return -EINVAL;
}
if (ret)
return ret;
/* Wait for the DeSSC PLL to stabilize. */
msleep(100);
ret = regmap_multi_reg_write(ctx->regmap, lt9211_pcr_seq,
ARRAY_SIZE(lt9211_pcr_seq));
if (ret)
return ret;
/* PCR stability test takes seconds. */
ret = regmap_read_poll_timeout(ctx->regmap, 0xd087, pval, pval & 0x8,
20000, 10000000);
if (ret)
dev_err(ctx->dev, "PCR unstable, ret=%i\n", ret);
return ret;
}
static int lt9211_configure_tx(struct lt9211 *ctx, bool jeida,
bool bpp24, bool de)
{
const struct reg_sequence system_lt9211_tx_phy_seq[] = {
/* DPI output disable */
{ 0x8262, 0x00 },
/* BIT(7) is LVDS dual-port */
{ 0x823b, 0x38 | (ctx->lvds_dual_link ? BIT(7) : 0) },
{ 0x823e, 0x92 },
{ 0x823f, 0x48 },
{ 0x8240, 0x31 },
{ 0x8243, 0x80 },
{ 0x8244, 0x00 },
{ 0x8245, 0x00 },
{ 0x8249, 0x00 },
{ 0x824a, 0x01 },
{ 0x824e, 0x00 },
{ 0x824f, 0x00 },
{ 0x8250, 0x00 },
{ 0x8253, 0x00 },
{ 0x8254, 0x01 },
/* LVDS channel order, Odd:Even 0x10..A:B, 0x40..B:A */
{ 0x8646, ctx->lvds_dual_link_even_odd_swap ? 0x40 : 0x10 },
{ 0x8120, 0x7b },
{ 0x816b, 0xff },
};
const struct reg_sequence system_lt9211_tx_dig_seq[] = {
{ 0x8559, 0x40 | (jeida ? BIT(7) : 0) |
(de ? BIT(5) : 0) | (bpp24 ? BIT(4) : 0) },
{ 0x855a, 0xaa },
{ 0x855b, 0xaa },
{ 0x855c, ctx->lvds_dual_link ? BIT(0) : 0 },
{ 0x85a1, 0x77 },
{ 0x8640, 0x40 },
{ 0x8641, 0x34 },
{ 0x8642, 0x10 },
{ 0x8643, 0x23 },
{ 0x8644, 0x41 },
{ 0x8645, 0x02 },
};
const struct reg_sequence system_lt9211_tx_pll_seq[] = {
/* TX PLL power down */
{ 0x8236, 0x01 },
{ 0x8237, ctx->lvds_dual_link ? 0x2a : 0x29 },
{ 0x8238, 0x06 },
{ 0x8239, 0x30 },
{ 0x823a, 0x8e },
{ 0x8737, 0x14 },
{ 0x8713, 0x00 },
{ 0x8713, 0x80 },
};
unsigned int pval;
int ret;
ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_phy_seq,
ARRAY_SIZE(system_lt9211_tx_phy_seq));
if (ret)
return ret;
ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_dig_seq,
ARRAY_SIZE(system_lt9211_tx_dig_seq));
if (ret)
return ret;
ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_pll_seq,
ARRAY_SIZE(system_lt9211_tx_pll_seq));
if (ret)
return ret;
ret = regmap_read_poll_timeout(ctx->regmap, 0x871f, pval, pval & 0x80,
10000, 1000000);
if (ret) {
dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret);
return ret;
}
ret = regmap_read_poll_timeout(ctx->regmap, 0x8720, pval, pval & 0x80,
10000, 1000000);
if (ret) {
dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret);
return ret;
}
return 0;
}
static void lt9211_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct lt9211 *ctx = bridge_to_lt9211(bridge);
struct drm_atomic_state *state = old_bridge_state->base.state;
const struct drm_bridge_state *bridge_state;
const struct drm_crtc_state *crtc_state;
const struct drm_display_mode *mode;
struct drm_connector *connector;
struct drm_crtc *crtc;
bool lvds_format_24bpp;
bool lvds_format_jeida;
u32 bus_flags;
int ret;
ret = regulator_enable(ctx->vccio);
if (ret) {
dev_err(ctx->dev, "Failed to enable vccio: %d\n", ret);
return;
}
/* Deassert reset */
gpiod_set_value(ctx->reset_gpio, 1);
usleep_range(20000, 21000); /* Very long post-reset delay. */
/* Get the LVDS format from the bridge state. */
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
bus_flags = bridge_state->output_bus_cfg.flags;
switch (bridge_state->output_bus_cfg.format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
lvds_format_24bpp = false;
lvds_format_jeida = true;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
lvds_format_24bpp = true;
lvds_format_jeida = true;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
lvds_format_24bpp = true;
lvds_format_jeida = false;
break;
default:
/*
* Some bridges still don't set the correct
* LVDS bus pixel format, use SPWG24 default
* format until those are fixed.
*/
lvds_format_24bpp = true;
lvds_format_jeida = false;
dev_warn(ctx->dev,
"Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
bridge_state->output_bus_cfg.format);
break;
}
/*
* Retrieve the CRTC adjusted mode. This requires a little dance to go
* from the bridge to the encoder, to the connector and to the CRTC.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
mode = &crtc_state->adjusted_mode;
ret = lt9211_read_chipid(ctx);
if (ret)
return;
ret = lt9211_system_init(ctx);
if (ret)
return;
ret = lt9211_configure_rx(ctx);
if (ret)
return;
ret = lt9211_autodetect_rx(ctx, mode);
if (ret)
return;
ret = lt9211_configure_timing(ctx, mode);
if (ret)
return;
ret = lt9211_configure_plls(ctx, mode);
if (ret)
return;
ret = lt9211_configure_tx(ctx, lvds_format_jeida, lvds_format_24bpp,
bus_flags & DRM_BUS_FLAG_DE_HIGH);
if (ret)
return;
dev_dbg(ctx->dev, "LT9211 enabled.\n");
}
static void lt9211_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct lt9211 *ctx = bridge_to_lt9211(bridge);
int ret;
/*
* Put the chip in reset, pull nRST line low,
* and assure lengthy 10ms reset low timing.
*/
gpiod_set_value(ctx->reset_gpio, 0);
usleep_range(10000, 11000); /* Very long reset duration. */
ret = regulator_disable(ctx->vccio);
if (ret)
dev_err(ctx->dev, "Failed to disable vccio: %d\n", ret);
regcache_mark_dirty(ctx->regmap);
}
static enum drm_mode_status
lt9211_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
/* LVDS output clock range 25..176 MHz */
if (mode->clock < 25000)
return MODE_CLOCK_LOW;
if (mode->clock > 176000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
#define MAX_INPUT_SEL_FORMATS 1
static u32 *
lt9211_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 const struct drm_bridge_funcs lt9211_funcs = {
.attach = lt9211_attach,
.mode_valid = lt9211_mode_valid,
.atomic_enable = lt9211_atomic_enable,
.atomic_disable = lt9211_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = lt9211_atomic_get_input_bus_fmts,
.atomic_reset = drm_atomic_helper_bridge_reset,
};
static int lt9211_parse_dt(struct lt9211 *ctx)
{
struct device_node *port2, *port3;
struct drm_bridge *panel_bridge;
struct device *dev = ctx->dev;
struct drm_panel *panel;
int dual_link;
int ret;
ctx->vccio = devm_regulator_get(dev, "vccio");
if (IS_ERR(ctx->vccio))
return dev_err_probe(dev, PTR_ERR(ctx->vccio),
"Failed to get supply 'vccio'\n");
ctx->lvds_dual_link = false;
ctx->lvds_dual_link_even_odd_swap = false;
port2 = of_graph_get_port_by_id(dev->of_node, 2);
port3 = of_graph_get_port_by_id(dev->of_node, 3);
dual_link = drm_of_lvds_get_dual_link_pixel_order(port2, port3);
of_node_put(port2);
of_node_put(port3);
if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {
ctx->lvds_dual_link = true;
/* Odd pixels to LVDS Channel A, even pixels to B */
ctx->lvds_dual_link_even_odd_swap = false;
} else if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
ctx->lvds_dual_link = true;
/* Even pixels to LVDS Channel A, odd pixels to B */
ctx->lvds_dual_link_even_odd_swap = true;
}
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge);
if (ret < 0)
return ret;
if (panel) {
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
}
ctx->panel_bridge = panel_bridge;
return 0;
}
static int lt9211_host_attach(struct lt9211 *ctx)
{
const struct mipi_dsi_device_info info = {
.type = "lt9211",
.channel = 0,
.node = NULL,
};
struct device *dev = ctx->dev;
struct device_node *host_node;
struct device_node *endpoint;
struct mipi_dsi_device *dsi;
struct mipi_dsi_host *host;
int dsi_lanes;
int ret;
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
host_node = of_graph_get_remote_port_parent(endpoint);
host = of_find_mipi_dsi_host_by_node(host_node);
of_node_put(host_node);
of_node_put(endpoint);
if (!host)
return -EPROBE_DEFER;
if (dsi_lanes < 0 || dsi_lanes > 4)
return -EINVAL;
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
if (IS_ERR(dsi))
return dev_err_probe(dev, PTR_ERR(dsi),
"failed to create dsi device\n");
ctx->dsi = dsi;
dsi->lanes = dsi_lanes;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_VIDEO_HSE;
ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "failed to attach dsi to host: %d\n", ret);
return ret;
}
return 0;
}
static int lt9211_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lt9211 *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->dev = dev;
/*
* Put the chip in reset, pull nRST line low,
* and assure lengthy 10ms reset low timing.
*/
ctx->reset_gpio = devm_gpiod_get_optional(ctx->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio))
return PTR_ERR(ctx->reset_gpio);
usleep_range(10000, 11000); /* Very long reset duration. */
ret = lt9211_parse_dt(ctx);
if (ret)
return ret;
ctx->regmap = devm_regmap_init_i2c(client, &lt9211_regmap_config);
if (IS_ERR(ctx->regmap))
return PTR_ERR(ctx->regmap);
dev_set_drvdata(dev, ctx);
i2c_set_clientdata(client, ctx);
ctx->bridge.funcs = &lt9211_funcs;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);
ret = lt9211_host_attach(ctx);
if (ret)
drm_bridge_remove(&ctx->bridge);
return ret;
}
static int lt9211_remove(struct i2c_client *client)
{
struct lt9211 *ctx = i2c_get_clientdata(client);
drm_bridge_remove(&ctx->bridge);
return 0;
}
static struct i2c_device_id lt9211_id[] = {
{ "lontium,lt9211" },
{},
};
MODULE_DEVICE_TABLE(i2c, lt9211_id);
static const struct of_device_id lt9211_match_table[] = {
{ .compatible = "lontium,lt9211" },
{},
};
MODULE_DEVICE_TABLE(of, lt9211_match_table);
static struct i2c_driver lt9211_driver = {
.probe = lt9211_probe,
.remove = lt9211_remove,
.id_table = lt9211_id,
.driver = {
.name = "lt9211",
.of_match_table = lt9211_match_table,
},
};
module_i2c_driver(lt9211_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Lontium LT9211 DSI/LVDS/DPI bridge driver");
MODULE_LICENSE("GPL");
+5 -2
View File
@@ -83,8 +83,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
drm_connector_attach_encoder(&panel_bridge->connector,
bridge->encoder);
if (connector->funcs->reset)
connector->funcs->reset(connector);
if (bridge->dev->registered) {
if (connector->funcs->reset)
connector->funcs->reset(connector);
drm_connector_register(connector);
}
return 0;
}
+10
View File
@@ -25,6 +25,16 @@ config DRM_DW_HDMI_I2S_AUDIO
Support the I2S Audio interface which is part of the Synopsys
Designware HDMI block.
config DRM_DW_HDMI_GP_AUDIO
tristate "Synopsys Designware GP Audio interface"
depends on DRM_DW_HDMI && SND
select SND_PCM
select SND_PCM_ELD
select SND_PCM_IEC958
help
Support the GP Audio interface which is part of the Synopsys
Designware HDMI block.
config DRM_DW_HDMI_CEC
tristate "Synopsis Designware CEC interface"
depends on DRM_DW_HDMI
+1
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* dw-hdmi-gp-audio.c
*
* Copyright 2020-2022 NXP
*/
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_edid.h>
#include <drm/drm_connector.h>
#include <sound/hdmi-codec.h>
#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_drm_eld.h>
#include <sound/pcm_iec958.h>
#include <sound/dmaengine_pcm.h>
#include "dw-hdmi-audio.h"
#define DRIVER_NAME "dw-hdmi-gp-audio"
#define DRV_NAME "hdmi-gp-audio"
struct snd_dw_hdmi {
struct dw_hdmi_audio_data data;
struct platform_device *audio_pdev;
unsigned int pos;
};
struct dw_hdmi_channel_conf {
u8 conf1;
u8 ca;
};
/*
* The default mapping of ALSA channels to HDMI channels and speaker
* allocation bits. Note that we can't do channel remapping here -
* channels must be in the same order.
*
* Mappings for alsa-lib pcm/surround*.conf files:
*
* Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
* Channels 2 4 6 6 6 8
*
* Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
*
* Number of ALSA channels
* ALSA Channel 2 3 4 5 6 7 8
* 0 FL:0 = = = = = =
* 1 FR:1 = = = = = =
* 2 FC:3 RL:4 LFE:2 = = =
* 3 RR:5 RL:4 FC:3 = =
* 4 RR:5 RL:4 = =
* 5 RR:5 = =
* 6 RC:6 =
* 7 RLC/FRC RLC/FRC
*/
static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
{ 0x03, 0x00 }, /* FL,FR */
{ 0x0b, 0x02 }, /* FL,FR,FC */
{ 0x33, 0x08 }, /* FL,FR,RL,RR */
{ 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
{ 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
{ 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
{ 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
};
static int audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
int ret = 0;
u8 ca;
dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
ca = default_hdmi_channel_config[params->channels - 2].ca;
dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
params->iec.status[0] & IEC958_AES0_NONAUDIO);
dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
return ret;
}
static void audio_shutdown(struct device *dev, void *data)
{
}
static int audio_mute_stream(struct device *dev, void *data,
bool enable, int direction)
{
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
int ret = 0;
if (!enable)
dw_hdmi_audio_enable(dw->data.hdmi);
else
dw_hdmi_audio_disable(dw->data.hdmi);
return ret;
}
static int audio_get_eld(struct device *dev, void *data,
u8 *buf, size_t len)
{
struct dw_hdmi_audio_data *audio = data;
u8 *eld;
eld = audio->get_eld(audio->hdmi);
if (eld)
memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
else
/* Pass en empty ELD if connector not available */
memset(buf, 0, len);
return 0;
}
static int audio_hook_plugged_cb(struct device *dev, void *data,
hdmi_codec_plugged_cb fn,
struct device *codec_dev)
{
struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
}
static const struct hdmi_codec_ops audio_codec_ops = {
.hw_params = audio_hw_params,
.audio_shutdown = audio_shutdown,
.mute_stream = audio_mute_stream,
.get_eld = audio_get_eld,
.hook_plugged_cb = audio_hook_plugged_cb,
};
static int snd_dw_hdmi_probe(struct platform_device *pdev)
{
struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
struct snd_dw_hdmi *dw;
const struct hdmi_codec_pdata codec_data = {
.i2s = 1,
.spdif = 0,
.ops = &audio_codec_ops,
.max_i2s_channels = 8,
.data = data,
};
dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
if (!dw)
return -ENOMEM;
dw->data = *data;
platform_set_drvdata(pdev, dw);
dw->audio_pdev = platform_device_register_data(&pdev->dev,
HDMI_CODEC_DRV_NAME, 1,
&codec_data,
sizeof(codec_data));
return PTR_ERR_OR_ZERO(dw->audio_pdev);
}
static int snd_dw_hdmi_remove(struct platform_device *pdev)
{
struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
platform_device_unregister(dw->audio_pdev);
return 0;
}
static struct platform_driver snd_dw_hdmi_driver = {
.probe = snd_dw_hdmi_probe,
.remove = snd_dw_hdmi_remove,
.driver = {
.name = DRIVER_NAME,
},
};
module_platform_driver(snd_dw_hdmi_driver);
MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);
+165 -21
View File
@@ -191,7 +191,10 @@ struct dw_hdmi {
spinlock_t audio_lock;
struct mutex audio_mutex;
unsigned int sample_non_pcm;
unsigned int sample_width;
unsigned int sample_rate;
unsigned int channels;
unsigned int audio_cts;
unsigned int audio_n;
bool audio_enable;
@@ -589,6 +592,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 4096;
else if (pixel_clk == 74176000 || pixel_clk == 148352000)
n = 11648;
else if (pixel_clk == 297000000)
n = 3072;
else
n = 4096;
n *= mult;
@@ -601,6 +606,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 17836;
else if (pixel_clk == 148352000)
n = 8918;
else if (pixel_clk == 297000000)
n = 4704;
else
n = 6272;
n *= mult;
@@ -615,6 +622,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 11648;
else if (pixel_clk == 148352000)
n = 5824;
else if (pixel_clk == 297000000)
n = 5120;
else
n = 6144;
n *= mult;
@@ -659,8 +668,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
/* Only compute CTS when using internal AHB audio */
if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
/* Compute CTS when using internal AHB audio or General Parallel audio*/
if ((config3 & HDMI_CONFIG3_AHBAUDDMA) || (config3 & HDMI_CONFIG3_GPAUD)) {
/*
* Compute the CTS value from the N value. Note that CTS and N
* can be up to 20 bits in total, so we need 64-bit math. Also
@@ -702,6 +711,22 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
mutex_unlock(&hdmi->audio_mutex);
}
void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width)
{
mutex_lock(&hdmi->audio_mutex);
hdmi->sample_width = width;
mutex_unlock(&hdmi->audio_mutex);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_width);
void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm)
{
mutex_lock(&hdmi->audio_mutex);
hdmi->sample_non_pcm = non_pcm;
mutex_unlock(&hdmi->audio_mutex);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{
mutex_lock(&hdmi->audio_mutex);
@@ -717,6 +742,7 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
u8 layout;
mutex_lock(&hdmi->audio_mutex);
hdmi->channels = cnt;
/*
* For >2 channel PCM audio, we need to select layout 1
@@ -765,6 +791,89 @@ static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
return hdmi->curr_conn->eld;
}
static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi)
{
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
int sample_freq = 0x2, org_sample_freq = 0xD;
int ch_mask = BIT(hdmi->channels) - 1;
switch (hdmi->sample_rate) {
case 32000:
sample_freq = 0x03;
org_sample_freq = 0x0C;
break;
case 44100:
sample_freq = 0x00;
org_sample_freq = 0x0F;
break;
case 48000:
sample_freq = 0x02;
org_sample_freq = 0x0D;
break;
case 88200:
sample_freq = 0x08;
org_sample_freq = 0x07;
break;
case 96000:
sample_freq = 0x0A;
org_sample_freq = 0x05;
break;
case 176400:
sample_freq = 0x0C;
org_sample_freq = 0x03;
break;
case 192000:
sample_freq = 0x0E;
org_sample_freq = 0x01;
break;
default:
break;
}
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
hdmi_enable_audio_clk(hdmi, true);
hdmi_writeb(hdmi, 0x1, HDMI_FC_AUDSCHNLS0);
hdmi_writeb(hdmi, hdmi->channels, HDMI_FC_AUDSCHNLS2);
hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS3);
hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS4);
hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS5);
hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS6);
hdmi_writeb(hdmi, (0x3 << 4) | sample_freq, HDMI_FC_AUDSCHNLS7);
hdmi_writeb(hdmi, (org_sample_freq << 4) | 0xb, HDMI_FC_AUDSCHNLS8);
hdmi_writeb(hdmi, ch_mask, HDMI_GP_CONF1);
hdmi_writeb(hdmi, 0x02, HDMI_GP_CONF2);
hdmi_writeb(hdmi, 0x01, HDMI_GP_CONF0);
hdmi_modb(hdmi, 0x3, 0x3, HDMI_FC_DATAUTO3);
/* hbr */
if (hdmi->sample_rate == 192000 && hdmi->channels == 8 &&
hdmi->sample_width == 32 && hdmi->sample_non_pcm)
hdmi_modb(hdmi, 0x01, 0x01, HDMI_GP_CONF2);
if (pdata->enable_audio)
pdata->enable_audio(hdmi,
hdmi->channels,
hdmi->sample_width,
hdmi->sample_rate,
hdmi->sample_non_pcm);
}
static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi)
{
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
hdmi_modb(hdmi, 0, 0x3, HDMI_FC_DATAUTO3);
if (pdata->disable_audio)
pdata->disable_audio(hdmi);
hdmi_enable_audio_clk(hdmi, false);
}
static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
{
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
@@ -1108,6 +1217,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
u8 val, vp_conf;
u8 clear_gcp_auto = 0;
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
@@ -1117,6 +1228,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
case 8:
color_depth = 4;
output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
clear_gcp_auto = 1;
break;
case 10:
color_depth = 5;
@@ -1136,6 +1248,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
case 0:
case 8:
remap_size = HDMI_VP_REMAP_YCC422_16bit;
clear_gcp_auto = 1;
break;
case 10:
remap_size = HDMI_VP_REMAP_YCC422_20bit;
@@ -1160,6 +1273,19 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
/* HDMI1.4b specification section 6.5.3:
* Source shall only send GCPs with non-zero CD to sinks
* that indicate support for Deep Color.
* GCP only transmit CD and do not handle AVMUTE, PP norDefault_Phase (yet).
* Disable Auto GCP when 24-bit color for sinks that not support Deep Color.
*/
val = hdmi_readb(hdmi, HDMI_FC_DATAUTO3);
if (clear_gcp_auto == 1)
val &= ~HDMI_FC_DATAUTO3_GCP_AUTO;
else
val |= HDMI_FC_DATAUTO3_GCP_AUTO;
hdmi_writeb(hdmi, val, HDMI_FC_DATAUTO3);
hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
@@ -1357,13 +1483,21 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_SELDIPIF_MASK);
}
void dw_hdmi_phy_reset(struct dw_hdmi *hdmi)
void dw_hdmi_phy_gen1_reset(struct dw_hdmi *hdmi)
{
/* PHY reset. The reset signal is active low on Gen1 PHYs. */
hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
}
EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen1_reset);
void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi)
{
/* PHY reset. The reset signal is active high on Gen2 PHYs. */
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
}
EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset);
EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_reset);
void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)
{
@@ -1517,7 +1651,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
if (phy->has_svsret)
dw_hdmi_phy_enable_svsret(hdmi, 1);
dw_hdmi_phy_reset(hdmi);
dw_hdmi_phy_gen2_reset(hdmi);
hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
@@ -2086,30 +2220,21 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
* then write one of the FC registers several times.
*
* The number of iterations matters and depends on the HDMI TX revision
* (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
* (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
* as needing the workaround, with 4 iterations for v1.30a and 1
* iteration for others.
* The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
* the workaround with a single iteration.
* The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have
* been identified as needing the workaround with a single iteration.
* (and possibly on the platform).
* 4 iterations for i.MX6Q(v1.30a) and 1 iteration for others.
* i.MX6DL (v1.31a), Allwinner SoCs (v1.32a), Rockchip RK3288 SoC (v2.00a),
* Amlogic Meson GX SoCs (v2.01a), RK3328/RK3399 SoCs (v2.11a)
* and i.MX8MPlus (v2.13a) have been identified as needing the workaround
* with a single iteration.
*/
switch (hdmi->version) {
case 0x130a:
count = 4;
break;
case 0x131a:
case 0x132a:
case 0x200a:
case 0x201a:
case 0x211a:
case 0x212a:
default:
count = 1;
break;
default:
return;
}
/* TMDS software reset */
@@ -3242,6 +3367,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->plat_data = plat_data;
hdmi->dev = dev;
hdmi->sample_rate = 48000;
hdmi->channels = 2;
hdmi->disabled = true;
hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
@@ -3465,6 +3591,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
pdevinfo.size_data = sizeof(audio);
pdevinfo.dma_mask = DMA_BIT_MASK(32);
hdmi->audio = platform_device_register_full(&pdevinfo);
} else if (iores && config3 & HDMI_CONFIG3_GPAUD) {
struct dw_hdmi_audio_data audio;
audio.phys = iores->start;
audio.base = hdmi->regs;
audio.irq = irq;
audio.hdmi = hdmi;
audio.get_eld = hdmi_audio_get_eld;
hdmi->enable_audio = dw_hdmi_gp_audio_enable;
hdmi->disable_audio = dw_hdmi_gp_audio_disable;
pdevinfo.name = "dw-hdmi-gp-audio";
pdevinfo.id = PLATFORM_DEVID_NONE;
pdevinfo.data = &audio;
pdevinfo.size_data = sizeof(audio);
pdevinfo.dma_mask = DMA_BIT_MASK(32);
hdmi->audio = platform_device_register_full(&pdevinfo);
}
if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) {
+14 -2
View File
@@ -158,8 +158,17 @@
#define HDMI_FC_SPDDEVICEINF 0x1062
#define HDMI_FC_AUDSCONF 0x1063
#define HDMI_FC_AUDSSTAT 0x1064
#define HDMI_FC_AUDSCHNLS7 0x106e
#define HDMI_FC_AUDSCHNLS8 0x106f
#define HDMI_FC_AUDSV 0x1065
#define HDMI_FC_AUDSU 0x1066
#define HDMI_FC_AUDSCHNLS0 0x1067
#define HDMI_FC_AUDSCHNLS1 0x1068
#define HDMI_FC_AUDSCHNLS2 0x1069
#define HDMI_FC_AUDSCHNLS3 0x106A
#define HDMI_FC_AUDSCHNLS4 0x106B
#define HDMI_FC_AUDSCHNLS5 0x106C
#define HDMI_FC_AUDSCHNLS6 0x106D
#define HDMI_FC_AUDSCHNLS7 0x106E
#define HDMI_FC_AUDSCHNLS8 0x106F
#define HDMI_FC_DATACH0FILL 0x1070
#define HDMI_FC_DATACH1FILL 0x1071
#define HDMI_FC_DATACH2FILL 0x1072
@@ -850,6 +859,9 @@ enum {
HDMI_FC_DATAUTO0_VSD_MASK = 0x08,
HDMI_FC_DATAUTO0_VSD_OFFSET = 3,
/* FC_DATAUTO3 field values */
HDMI_FC_DATAUTO3_GCP_AUTO = 0x04,
/* PHY_CONF0 field values */
HDMI_PHY_CONF0_PDZ_MASK = 0x80,
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
+28 -5
View File
@@ -527,6 +527,31 @@ unlock:
return ret;
}
/**
* drm_dp_dpcd_probe() - probe a given DPCD address with a 1-byte read access
* @aux: DisplayPort AUX channel (SST)
* @offset: address of the register to probe
*
* Probe the provided DPCD address by reading 1 byte from it. The function can
* be used to trigger some side-effect the read access has, like waking up the
* sink, without the need for the read-out value.
*
* Returns 0 if the read access suceeded, or a negative error code on failure.
*/
int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset)
{
u8 buffer;
int ret;
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, 1);
WARN_ON(ret == 0);
drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, ret);
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL(drm_dp_dpcd_probe);
/**
* drm_dp_dpcd_read() - read a series of bytes from the DPCD
* @aux: DisplayPort AUX channel (SST or MST)
@@ -559,10 +584,9 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
* monitor doesn't power down exactly after the throw away read.
*/
if (!aux->is_remote) {
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
buffer, 1);
if (ret != 1)
goto out;
ret = drm_dp_dpcd_probe(aux, DP_DPCD_REV);
if (ret < 0)
return ret;
}
if (aux->is_remote)
@@ -571,7 +595,6 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
buffer, size);
out:
drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);
return ret;
}
+3
View File
@@ -665,6 +665,9 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
if (start + size == end)
return __drm_buddy_alloc_range(mm, start, size, blocks);
if (!IS_ALIGNED(size, min_page_size))
return -EINVAL;
pages = size >> ilog2(mm->chunk_size);
order = fls(pages) - 1;
min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
+6 -6
View File
@@ -297,15 +297,15 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
return false;
}
saved_mode = crtc->mode;
saved_hwmode = crtc->hwmode;
drm_mode_init(&saved_mode, &crtc->mode);
drm_mode_init(&saved_hwmode, &crtc->hwmode);
saved_x = crtc->x;
saved_y = crtc->y;
/* Update crtc values up front so the driver can rely on them for mode
* setting.
*/
crtc->mode = *mode;
drm_mode_copy(&crtc->mode, mode);
crtc->x = x;
crtc->y = y;
@@ -341,7 +341,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
}
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
crtc->hwmode = *adjusted_mode;
drm_mode_copy(&crtc->hwmode, adjusted_mode);
/* Prepare the encoders and CRTCs before setting the mode. */
drm_for_each_encoder(encoder, dev) {
@@ -411,8 +411,8 @@ done:
drm_mode_destroy(dev, adjusted_mode);
if (!ret) {
crtc->enabled = saved_enabled;
crtc->mode = saved_mode;
crtc->hwmode = saved_hwmode;
drm_mode_copy(&crtc->mode, &saved_mode);
drm_mode_copy(&crtc->hwmode, &saved_hwmode);
crtc->x = saved_x;
crtc->y = saved_y;
}
+231 -127
View File
@@ -1568,6 +1568,38 @@ static const struct drm_display_mode edid_4k_modes[] = {
/*** DDC fetch and block validation ***/
static int edid_extension_block_count(const struct edid *edid)
{
return edid->extensions;
}
static int edid_block_count(const struct edid *edid)
{
return edid_extension_block_count(edid) + 1;
}
static int edid_size_by_blocks(int num_blocks)
{
return num_blocks * EDID_LENGTH;
}
static int edid_size(const struct edid *edid)
{
return edid_size_by_blocks(edid_block_count(edid));
}
static const void *edid_block_data(const struct edid *edid, int index)
{
BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH);
return edid + index;
}
static const void *edid_extension_block_data(const struct edid *edid, int index)
{
return edid_block_data(edid, index + 1);
}
static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
@@ -1632,9 +1664,9 @@ static int edid_block_tag(const void *_block)
return block[0];
}
static bool edid_is_zero(const void *edid, int length)
static bool edid_block_is_zero(const void *edid)
{
return !memchr_inv(edid, 0, length);
return !memchr_inv(edid, 0, EDID_LENGTH);
}
/**
@@ -1654,8 +1686,8 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
return false;
if (edid1) {
edid1_len = EDID_LENGTH * (1 + edid1->extensions);
edid2_len = EDID_LENGTH * (1 + edid2->extensions);
edid1_len = edid_size(edid1);
edid2_len = edid_size(edid2);
if (edid1_len != edid2_len)
return false;
@@ -1670,7 +1702,9 @@ EXPORT_SYMBOL(drm_edid_are_equal);
enum edid_block_status {
EDID_BLOCK_OK = 0,
EDID_BLOCK_READ_FAIL,
EDID_BLOCK_NULL,
EDID_BLOCK_ZERO,
EDID_BLOCK_HEADER_CORRUPT,
EDID_BLOCK_HEADER_REPAIR,
EDID_BLOCK_HEADER_FIXED,
@@ -1689,15 +1723,23 @@ static enum edid_block_status edid_block_check(const void *_block,
if (is_base_block) {
int score = drm_edid_header_is_valid(block);
if (score < clamp(edid_fixup, 0, 8))
return EDID_BLOCK_HEADER_CORRUPT;
if (score < clamp(edid_fixup, 0, 8)) {
if (edid_block_is_zero(block))
return EDID_BLOCK_ZERO;
else
return EDID_BLOCK_HEADER_CORRUPT;
}
if (score < 8)
return EDID_BLOCK_HEADER_REPAIR;
}
if (edid_block_compute_checksum(block) != edid_block_get_checksum(block))
return EDID_BLOCK_CHECKSUM;
if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) {
if (edid_block_is_zero(block))
return EDID_BLOCK_ZERO;
else
return EDID_BLOCK_CHECKSUM;
}
if (is_base_block) {
if (block->version != 1)
@@ -1720,6 +1762,70 @@ static bool edid_block_valid(const void *block, bool base)
edid_block_tag(block));
}
static void edid_block_status_print(enum edid_block_status status,
const struct edid *block,
int block_num)
{
switch (status) {
case EDID_BLOCK_OK:
break;
case EDID_BLOCK_READ_FAIL:
pr_debug("EDID block %d read failed\n", block_num);
break;
case EDID_BLOCK_NULL:
pr_debug("EDID block %d pointer is NULL\n", block_num);
break;
case EDID_BLOCK_ZERO:
pr_notice("EDID block %d is all zeroes\n", block_num);
break;
case EDID_BLOCK_HEADER_CORRUPT:
pr_notice("EDID has corrupt header\n");
break;
case EDID_BLOCK_HEADER_REPAIR:
pr_debug("EDID corrupt header needs repair\n");
break;
case EDID_BLOCK_HEADER_FIXED:
pr_debug("EDID corrupt header fixed\n");
break;
case EDID_BLOCK_CHECKSUM:
if (edid_block_status_valid(status, edid_block_tag(block))) {
pr_debug("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d, ignoring\n",
block_num, edid_block_tag(block),
edid_block_compute_checksum(block));
} else {
pr_notice("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d\n",
block_num, edid_block_tag(block),
edid_block_compute_checksum(block));
}
break;
case EDID_BLOCK_VERSION:
pr_notice("EDID has major version %d, instead of 1\n",
block->version);
break;
default:
WARN(1, "EDID block %d unknown edid block status code %d\n",
block_num, status);
break;
}
}
static void edid_block_dump(const char *level, const void *block, int block_num)
{
enum edid_block_status status;
char prefix[20];
status = edid_block_check(block, block_num == 0);
if (status == EDID_BLOCK_ZERO)
sprintf(prefix, "\t[%02x] ZERO ", block_num);
else if (!edid_block_status_valid(status, edid_block_tag(block)))
sprintf(prefix, "\t[%02x] BAD ", block_num);
else
sprintf(prefix, "\t[%02x] GOOD ", block_num);
print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
block, EDID_LENGTH, false);
}
/**
* drm_edid_block_valid - Sanity check the EDID block (base or extension)
* @raw_edid: pointer to raw EDID block
@@ -1766,33 +1872,14 @@ bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
*edid_corrupt = true;
}
edid_block_status_print(status, block, block_num);
/* Determine whether we can use this block with this status. */
valid = edid_block_status_valid(status, edid_block_tag(block));
/* Some fairly random status printouts. */
if (status == EDID_BLOCK_CHECKSUM) {
if (valid) {
DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n",
edid_block_compute_checksum(block));
DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n");
} else if (print_bad_edid) {
DRM_NOTE("EDID block checksum is invalid, remainder is %d\n",
edid_block_compute_checksum(block));
}
} else if (status == EDID_BLOCK_VERSION) {
DRM_NOTE("EDID has major version %d, instead of 1\n",
block->version);
}
if (!valid && print_bad_edid) {
if (edid_is_zero(block, EDID_LENGTH)) {
pr_notice("EDID block is all zeroes\n");
} else {
pr_notice("Raw EDID:\n");
print_hex_dump(KERN_NOTICE,
" \t", DUMP_PREFIX_NONE, 16, 1,
block, EDID_LENGTH, false);
}
if (!valid && print_bad_edid && status != EDID_BLOCK_ZERO) {
pr_notice("Raw EDID:\n");
edid_block_dump(KERN_NOTICE, block, block_num);
}
return valid;
@@ -1810,14 +1897,16 @@ EXPORT_SYMBOL(drm_edid_block_valid);
bool drm_edid_is_valid(struct edid *edid)
{
int i;
u8 *raw = (u8 *)edid;
if (!edid)
return false;
for (i = 0; i <= edid->extensions; i++)
if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL))
for (i = 0; i < edid_block_count(edid); i++) {
void *block = (void *)edid_block_data(edid, i);
if (!drm_edid_block_valid(block, i, true, NULL))
return false;
}
return true;
}
@@ -1830,13 +1919,13 @@ static struct edid *edid_filter_invalid_blocks(const struct edid *edid,
int valid_extensions = edid->extensions - invalid_blocks;
int i;
new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL);
new = kmalloc(edid_size_by_blocks(valid_extensions + 1), GFP_KERNEL);
if (!new)
goto out;
dest_block = new;
for (i = 0; i <= edid->extensions; i++) {
const void *block = edid + i;
for (i = 0; i < edid_block_count(edid); i++) {
const void *block = edid_block_data(edid, i);
if (edid_block_valid(block, i == 0))
memcpy(dest_block++, block, EDID_LENGTH);
@@ -1916,7 +2005,7 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
}
static void connector_bad_edid(struct drm_connector *connector,
u8 *edid, int num_blocks)
const struct edid *edid, int num_blocks)
{
int i;
u8 last_block;
@@ -1927,32 +2016,19 @@ static void connector_bad_edid(struct drm_connector *connector,
* of 0x7e in the EDID of the _index_ of the last block in the
* combined chunk of memory.
*/
last_block = edid[0x7e];
last_block = edid->extensions;
/* Calculate real checksum for the last edid extension block data */
if (last_block < num_blocks)
connector->real_edid_checksum =
edid_block_compute_checksum(edid + last_block * EDID_LENGTH);
edid_block_compute_checksum(edid + last_block);
if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
return;
drm_dbg_kms(connector->dev, "%s: EDID is invalid:\n", connector->name);
for (i = 0; i < num_blocks; i++) {
u8 *block = edid + i * EDID_LENGTH;
char prefix[20];
if (edid_is_zero(block, EDID_LENGTH))
sprintf(prefix, "\t[%02x] ZERO ", i);
else if (!drm_edid_block_valid(block, i, false, NULL))
sprintf(prefix, "\t[%02x] BAD ", i);
else
sprintf(prefix, "\t[%02x] GOOD ", i);
print_hex_dump(KERN_DEBUG,
prefix, DUMP_PREFIX_NONE, 16, 1,
block, EDID_LENGTH, false);
}
for (i = 0; i < num_blocks; i++)
edid_block_dump(KERN_DEBUG, edid + i, i);
}
/* Get override or firmware EDID */
@@ -1999,43 +2075,39 @@ int drm_add_override_edid_modes(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_add_override_edid_modes);
static struct edid *drm_do_get_edid_base_block(struct drm_connector *connector,
int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
size_t len),
void *data)
typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len);
static enum edid_block_status edid_block_read(void *block, unsigned int block_num,
read_block_fn read_block,
void *context)
{
int *null_edid_counter = connector ? &connector->null_edid_counter : NULL;
bool *edid_corrupt = connector ? &connector->edid_corrupt : NULL;
void *edid;
enum edid_block_status status;
bool is_base_block = block_num == 0;
int try;
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (edid == NULL)
return NULL;
/* base block fetch */
for (try = 0; try < 4; try++) {
if (get_edid_block(data, edid, 0, EDID_LENGTH))
goto out;
if (drm_edid_block_valid(edid, 0, false, edid_corrupt))
break;
if (try == 0 && edid_is_zero(edid, EDID_LENGTH)) {
if (null_edid_counter)
(*null_edid_counter)++;
goto carp;
if (read_block(context, block, block_num, EDID_LENGTH))
return EDID_BLOCK_READ_FAIL;
status = edid_block_check(block, is_base_block);
if (status == EDID_BLOCK_HEADER_REPAIR) {
edid_header_fix(block);
/* Retry with fixed header, update status if that worked. */
status = edid_block_check(block, is_base_block);
if (status == EDID_BLOCK_OK)
status = EDID_BLOCK_HEADER_FIXED;
}
if (edid_block_status_valid(status, edid_block_tag(block)))
break;
/* Fail early for unrepairable base block all zeros. */
if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO)
break;
}
if (try == 4)
goto carp;
return edid;
carp:
if (connector)
connector_bad_edid(connector, edid, 1);
out:
kfree(edid);
return NULL;
return status;
}
/**
@@ -2059,53 +2131,74 @@ out:
* Return: Pointer to valid EDID or NULL if we couldn't find any.
*/
struct edid *drm_do_get_edid(struct drm_connector *connector,
int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
size_t len),
void *data)
read_block_fn read_block,
void *context)
{
int j, invalid_blocks = 0;
struct edid *edid, *new, *override;
enum edid_block_status status;
int i, invalid_blocks = 0;
struct edid *edid, *new;
override = drm_get_override_edid(connector);
if (override)
return override;
edid = drm_get_override_edid(connector);
if (edid)
goto ok;
edid = drm_do_get_edid_base_block(connector, get_edid_block, data);
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid)
return NULL;
if (edid->extensions == 0)
return edid;
status = edid_block_read(edid, 0, read_block, context);
new = krealloc(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
edid_block_status_print(status, edid, 0);
if (status == EDID_BLOCK_READ_FAIL)
goto fail;
/* FIXME: Clarify what a corrupt EDID actually means. */
if (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION)
connector->edid_corrupt = false;
else
connector->edid_corrupt = true;
if (!edid_block_status_valid(status, edid_block_tag(edid))) {
if (status == EDID_BLOCK_ZERO)
connector->null_edid_counter++;
connector_bad_edid(connector, edid, 1);
goto fail;
}
if (!edid_extension_block_count(edid))
goto ok;
new = krealloc(edid, edid_size(edid), GFP_KERNEL);
if (!new)
goto out;
goto fail;
edid = new;
for (j = 1; j <= edid->extensions; j++) {
void *block = edid + j;
int try;
for (i = 1; i < edid_block_count(edid); i++) {
void *block = (void *)edid_block_data(edid, i);
for (try = 0; try < 4; try++) {
if (get_edid_block(data, block, j, EDID_LENGTH))
goto out;
if (drm_edid_block_valid(block, j, false, NULL))
break;
}
status = edid_block_read(block, i, read_block, context);
if (try == 4)
edid_block_status_print(status, block, i);
if (!edid_block_status_valid(status, edid_block_tag(block))) {
if (status == EDID_BLOCK_READ_FAIL)
goto fail;
invalid_blocks++;
}
}
if (invalid_blocks) {
connector_bad_edid(connector, (u8 *)edid, edid->extensions + 1);
connector_bad_edid(connector, edid, edid_block_count(edid));
edid = edid_filter_invalid_blocks(edid, invalid_blocks);
}
ok:
return edid;
out:
fail:
kfree(edid);
return NULL;
}
@@ -2199,20 +2292,27 @@ static u32 edid_extract_panel_id(const struct edid *edid)
u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
{
const struct edid *edid;
u32 panel_id;
edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter);
enum edid_block_status status;
void *base_block;
u32 panel_id = 0;
/*
* There are no manufacturer IDs of 0, so if there is a problem reading
* the EDID then we'll just return 0.
*/
if (!edid)
base_block = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!base_block)
return 0;
panel_id = edid_extract_panel_id(edid);
kfree(edid);
status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter);
edid_block_status_print(status, base_block, 0);
if (edid_block_status_valid(status, edid_block_tag(base_block)))
panel_id = edid_extract_panel_id(base_block);
kfree(base_block);
return panel_id;
}
@@ -2255,7 +2355,7 @@ EXPORT_SYMBOL(drm_get_edid_switcheroo);
*/
struct edid *drm_edid_duplicate(const struct edid *edid)
{
return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
return kmemdup(edid, edid_size(edid), GFP_KERNEL);
}
EXPORT_SYMBOL(drm_edid_duplicate);
@@ -2439,8 +2539,8 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
cb(&(edid->detailed_timings[i]), closure);
for (i = 1; i <= edid->extensions; i++) {
const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH);
for (i = 0; i < edid_extension_block_count(edid); i++) {
const u8 *ext = edid_extension_block_data(edid, i);
switch (*ext) {
case CEA_EXT:
@@ -3410,17 +3510,17 @@ const u8 *drm_find_edid_extension(const struct edid *edid,
int i;
/* No EDID or EDID extensions */
if (edid == NULL || edid->extensions == 0)
if (!edid || !edid_extension_block_count(edid))
return NULL;
/* Find CEA extension */
for (i = *ext_index; i < edid->extensions; i++) {
edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1);
for (i = *ext_index; i < edid_extension_block_count(edid); i++) {
edid_ext = edid_extension_block_data(edid, i);
if (edid_block_tag(edid_ext) == ext_id)
break;
}
if (i >= edid->extensions)
if (i >= edid_extension_block_count(edid))
return NULL;
*ext_index = i + 1;
@@ -3551,9 +3651,11 @@ static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_m
match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
struct drm_display_mode cea_mode = *cea_mode_for_vic(vic);
struct drm_display_mode cea_mode;
unsigned int clock1, clock2;
drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
/* Check both 60Hz and 59.94Hz */
clock1 = cea_mode.clock;
clock2 = cea_mode_alternate_clock(&cea_mode);
@@ -3590,9 +3692,11 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
struct drm_display_mode cea_mode = *cea_mode_for_vic(vic);
struct drm_display_mode cea_mode;
unsigned int clock1, clock2;
drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
/* Check both 60Hz and 59.94Hz */
clock1 = cea_mode.clock;
clock2 = cea_mode_alternate_clock(&cea_mode);
+2 -1
View File
@@ -771,7 +771,8 @@ long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
return -EINVAL;
}
ret = dma_resv_wait_timeout(obj->resv, wait_all, true, timeout);
ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(wait_all),
true, timeout);
if (ret == 0)
ret = -ETIME;
else if (ret > 0)
+1 -1
View File
@@ -151,7 +151,7 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_st
return 0;
obj = drm_gem_fb_get_obj(state->fb, 0);
ret = dma_resv_get_singleton(obj->resv, false, &fence);
ret = dma_resv_get_singleton(obj->resv, DMA_RESV_USAGE_WRITE, &fence);
if (ret)
return ret;
+3 -1
View File
@@ -837,7 +837,9 @@ EXPORT_SYMBOL(drm_mode_vrefresh);
void drm_mode_get_hv_timing(const struct drm_display_mode *mode,
int *hdisplay, int *vdisplay)
{
struct drm_display_mode adjusted = *mode;
struct drm_display_mode adjusted;
drm_mode_init(&adjusted, mode);
drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
*hdisplay = adjusted.crtc_hdisplay;
+1 -1
View File
@@ -644,7 +644,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
vblank->linedur_ns = linedur_ns;
vblank->framedur_ns = framedur_ns;
vblank->hwmode = *mode;
drm_mode_copy(&vblank->hwmode, mode);
drm_dbg_core(dev,
"crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
+4 -2
View File
@@ -380,12 +380,14 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
}
if (op & ETNA_PREP_NOSYNC) {
if (!dma_resv_test_signaled(obj->resv, write))
if (!dma_resv_test_signaled(obj->resv,
dma_resv_usage_rw(write)))
return -EBUSY;
} else {
unsigned long remain = etnaviv_timeout_to_jiffies(timeout);
ret = dma_resv_wait_timeout(obj->resv, write, true, remain);
ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write),
true, remain);
if (ret <= 0)
return ret == 0 ? -ETIMEDOUT : ret;
}
+3 -7
View File
@@ -202,14 +202,10 @@ static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
for (i = 0; i < submit->nr_bos; i++) {
struct drm_gem_object *obj = &submit->bos[i].obj->base;
bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE;
if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
dma_resv_add_excl_fence(obj->resv,
submit->out_fence);
else
dma_resv_add_shared_fence(obj->resv,
submit->out_fence);
dma_resv_add_fence(obj->resv, submit->out_fence, write ?
DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
submit_unlock_object(submit, i);
}
}
+2 -3
View File
@@ -396,9 +396,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)
drm_for_each_connector_iter(connector, &conn_iter) {
gma_encoder = gma_attached_encoder(connector);
switch (gma_encoder->type) {
case INTEL_OUTPUT_LVDS:
case INTEL_OUTPUT_MIPI:
if (gma_encoder->type == INTEL_OUTPUT_LVDS ||
gma_encoder->type == INTEL_OUTPUT_MIPI) {
ret = gma_backlight_init(dev);
break;
}
@@ -1047,7 +1047,8 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
if (ret < 0)
goto unpin_fb;
dma_resv_iter_begin(&cursor, obj->base.resv, false);
dma_resv_iter_begin(&cursor, obj->base.resv,
DMA_RESV_USAGE_WRITE);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
add_rps_boost_after_vblank(new_plane_state->hw.crtc,
fence);
+5 -5
View File
@@ -138,21 +138,21 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
* Alternatively, we can trade that extra information on read/write
* activity with
* args->busy =
* !dma_resv_test_signaled(obj->resv, true);
* !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ);
* to report the overall busyness. This is what the wait-ioctl does.
*
*/
args->busy = 0;
dma_resv_iter_begin(&cursor, obj->base.resv, true);
dma_resv_iter_begin(&cursor, obj->base.resv, DMA_RESV_USAGE_READ);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (dma_resv_iter_is_restarted(&cursor))
args->busy = 0;
if (dma_resv_iter_is_exclusive(&cursor))
/* Translate the exclusive fence to the READ *and* WRITE engine */
if (dma_resv_iter_usage(&cursor) <= DMA_RESV_USAGE_WRITE)
/* Translate the write fences to the READ *and* WRITE engine */
args->busy |= busy_check_writer(fence);
else
/* Translate shared fences to READ set of engines */
/* Translate read fences to READ set of engines */
args->busy |= busy_check_reader(fence);
}
dma_resv_iter_end(&cursor);
+2 -1
View File
@@ -116,7 +116,8 @@ bool i915_gem_clflush_object(struct drm_i915_gem_object *obj,
obj->base.resv, NULL, true,
i915_fence_timeout(i915),
I915_FENCE_GFP);
dma_resv_add_excl_fence(obj->base.resv, &clflush->base.dma);
dma_resv_add_fence(obj->base.resv, &clflush->base.dma,
DMA_RESV_USAGE_KERNEL);
dma_fence_work_commit(&clflush->base);
/*
* We must have successfully populated the pages(since we are
+1 -1
View File
@@ -68,7 +68,7 @@ bool __i915_gem_object_is_lmem(struct drm_i915_gem_object *obj)
struct intel_memory_region *mr = READ_ONCE(obj->mm.region);
#ifdef CONFIG_LOCKDEP
GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, true) &&
GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP) &&
i915_gem_object_evictable(obj));
#endif
return mr && (mr->type == INTEL_MEMORY_LOCAL ||
+12 -30
View File
@@ -742,30 +742,19 @@ static const struct drm_gem_object_funcs i915_gem_object_funcs = {
/**
* i915_gem_object_get_moving_fence - Get the object's moving fence if any
* @obj: The object whose moving fence to get.
* @fence: The resulting fence
*
* A non-signaled moving fence means that there is an async operation
* pending on the object that needs to be waited on before setting up
* any GPU- or CPU PTEs to the object's pages.
*
* Return: A refcounted pointer to the object's moving fence if any,
* NULL otherwise.
* Return: Negative error code or 0 for success.
*/
struct dma_fence *
i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj)
int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj,
struct dma_fence **fence)
{
return dma_fence_get(i915_gem_to_ttm(obj)->moving);
}
void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
struct dma_fence *fence)
{
struct dma_fence **moving = &i915_gem_to_ttm(obj)->moving;
if (*moving == fence)
return;
dma_fence_put(*moving);
*moving = dma_fence_get(fence);
return dma_resv_get_singleton(obj->base.resv, DMA_RESV_USAGE_KERNEL,
fence);
}
/**
@@ -783,23 +772,16 @@ void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,
bool intr)
{
struct dma_fence *fence = i915_gem_to_ttm(obj)->moving;
int ret;
long ret;
assert_object_held(obj);
if (!fence)
return 0;
ret = dma_fence_wait(fence, intr);
if (ret)
return ret;
ret = dma_resv_wait_timeout(obj->base. resv, DMA_RESV_USAGE_KERNEL,
intr, MAX_SCHEDULE_TIMEOUT);
if (!ret)
ret = -ETIME;
if (fence->error)
return fence->error;
i915_gem_to_ttm(obj)->moving = NULL;
dma_fence_put(fence);
return 0;
return ret < 0 ? ret : 0;
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+2 -6
View File
@@ -520,12 +520,8 @@ i915_gem_object_finish_access(struct drm_i915_gem_object *obj)
i915_gem_object_unpin_pages(obj);
}
struct dma_fence *
i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj);
void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
struct dma_fence *fence);
int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj,
struct dma_fence **fence);
int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,
bool intr);
+3 -17
View File
@@ -467,19 +467,6 @@ out:
return fence;
}
static int
prev_deps(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx,
struct i915_deps *deps)
{
int ret;
ret = i915_deps_add_dependency(deps, bo->moving, ctx);
if (!ret)
ret = i915_deps_add_resv(deps, bo->base.resv, ctx);
return ret;
}
/**
* i915_ttm_move - The TTM move callback used by i915.
* @bo: The buffer object.
@@ -534,7 +521,7 @@ int i915_ttm_move(struct ttm_buffer_object *bo, bool evict,
struct i915_deps deps;
i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
ret = prev_deps(bo, ctx, &deps);
ret = i915_deps_add_resv(&deps, bo->base.resv, ctx);
if (ret) {
i915_refct_sgt_put(dst_rsgt);
return ret;
@@ -637,9 +624,8 @@ int i915_gem_obj_copy_ttm(struct drm_i915_gem_object *dst,
if (IS_ERR_OR_NULL(copy_fence))
return PTR_ERR_OR_ZERO(copy_fence);
dma_resv_add_excl_fence(dst_bo->base.resv, copy_fence);
dma_resv_add_shared_fence(src_bo->base.resv, copy_fence);
dma_resv_add_fence(dst_bo->base.resv, copy_fence, DMA_RESV_USAGE_WRITE);
dma_resv_add_fence(src_bo->base.resv, copy_fence, DMA_RESV_USAGE_READ);
dma_fence_put(copy_fence);
return 0;
+1 -1
View File
@@ -86,7 +86,7 @@ static bool i915_gem_userptr_invalidate(struct mmu_interval_notifier *mni,
return true;
/* we will unbind on next submission, still have userptr pins */
r = dma_resv_wait_timeout(obj->base.resv, true, false,
r = dma_resv_wait_timeout(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP, false,
MAX_SCHEDULE_TIMEOUT);
if (r <= 0)
drm_err(&i915->drm, "(%ld) failed to wait for idle\n", r);
+4 -2
View File
@@ -40,7 +40,8 @@ i915_gem_object_wait_reservation(struct dma_resv *resv,
struct dma_fence *fence;
long ret = timeout ?: 1;
dma_resv_iter_begin(&cursor, resv, flags & I915_WAIT_ALL);
dma_resv_iter_begin(&cursor, resv,
dma_resv_usage_rw(flags & I915_WAIT_ALL));
dma_resv_for_each_fence_unlocked(&cursor, fence) {
ret = i915_gem_object_wait_fence(fence, flags, timeout);
if (ret <= 0)
@@ -117,7 +118,8 @@ i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_iter_begin(&cursor, obj->base.resv, flags & I915_WAIT_ALL);
dma_resv_iter_begin(&cursor, obj->base.resv,
dma_resv_usage_rw(flags & I915_WAIT_ALL));
dma_resv_for_each_fence_unlocked(&cursor, fence)
i915_gem_fence_wait_priority(fence, attr);
dma_resv_iter_end(&cursor);
@@ -219,7 +219,8 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915,
goto out_detach;
}
timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ);
timeout = dma_resv_wait_timeout(dmabuf->resv, DMA_RESV_USAGE_WRITE,
true, 5 * HZ);
if (!timeout) {
pr_err("dmabuf wait for exclusive fence timed out.\n");
timeout = -ETIME;
@@ -218,9 +218,8 @@ static int __igt_lmem_pages_migrate(struct intel_gt *gt,
if (rq) {
err = dma_resv_reserve_fences(obj->base.resv, 1);
if (!err)
dma_resv_add_excl_fence(obj->base.resv,
&rq->fence);
i915_gem_object_set_moving_fence(obj, &rq->fence);
dma_resv_add_fence(obj->base.resv, &rq->fence,
DMA_RESV_USAGE_KERNEL);
i915_request_put(rq);
}
if (err)
@@ -1221,8 +1221,8 @@ static int __igt_mmap_migrate(struct intel_memory_region **placements,
expand32(POISON_INUSE), &rq);
i915_gem_object_unpin_pages(obj);
if (rq) {
dma_resv_add_excl_fence(obj->base.resv, &rq->fence);
i915_gem_object_set_moving_fence(obj, &rq->fence);
dma_resv_add_fence(obj->base.resv, &rq->fence,
DMA_RESV_USAGE_KERNEL);
i915_request_put(rq);
}
i915_gem_object_unlock(obj);
+1 -1
View File
@@ -226,7 +226,7 @@ int i915_deps_add_resv(struct i915_deps *deps, struct dma_resv *resv,
struct dma_fence *fence;
dma_resv_assert_held(resv);
dma_resv_for_each_fence(&iter, resv, true, fence) {
dma_resv_for_each_fence(&iter, resv, dma_resv_usage_rw(true), fence) {
int ret = i915_deps_add_dependency(deps, fence, ctx);
if (ret)
+2 -1
View File
@@ -1598,7 +1598,8 @@ i915_request_await_object(struct i915_request *to,
struct dma_fence *fence;
int ret = 0;
dma_resv_for_each_fence(&cursor, obj->base.resv, write, fence) {
dma_resv_for_each_fence(&cursor, obj->base.resv,
dma_resv_usage_rw(write), fence) {
ret = i915_request_await_dma_fence(to, fence);
if (ret)
break;
+1 -1
View File
@@ -585,7 +585,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
debug_fence_assert(fence);
might_sleep_if(gfpflags_allow_blocking(gfp));
dma_resv_iter_begin(&cursor, resv, write);
dma_resv_iter_begin(&cursor, resv, dma_resv_usage_rw(write));
dma_resv_for_each_fence_unlocked(&cursor, f) {
pending = i915_sw_fence_await_dma_fence(fence, f, timeout,
gfp);
+13 -4
View File
@@ -1357,10 +1357,17 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,
if (err)
return err;
if (vma->obj) {
err = i915_gem_object_get_moving_fence(vma->obj, &moving);
if (err)
return err;
} else {
moving = NULL;
}
if (flags & PIN_GLOBAL)
wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
moving = vma->obj ? i915_gem_object_get_moving_fence(vma->obj) : NULL;
if (flags & vma->vm->bind_async_flags || moving) {
/* lock VM */
err = i915_vm_lock_objects(vma->vm, ww);
@@ -1826,7 +1833,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,
}
if (fence) {
dma_resv_add_excl_fence(vma->obj->base.resv, fence);
dma_resv_add_fence(vma->obj->base.resv, fence,
DMA_RESV_USAGE_WRITE);
obj->write_domain = I915_GEM_DOMAIN_RENDER;
obj->read_domains = 0;
}
@@ -1838,7 +1846,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,
}
if (fence) {
dma_resv_add_shared_fence(vma->obj->base.resv, fence);
dma_resv_add_fence(vma->obj->base.resv, fence,
DMA_RESV_USAGE_READ);
obj->write_domain = 0;
}
}
@@ -2078,7 +2087,7 @@ int i915_vma_unbind_async(struct i915_vma *vma, bool trylock_vm)
goto out_rpm;
}
dma_resv_add_shared_fence(obj->base.resv, fence);
dma_resv_add_fence(obj->base.resv, fence, DMA_RESV_USAGE_READ);
dma_fence_put(fence);
out_rpm:
@@ -1056,7 +1056,8 @@ static int igt_lmem_write_cpu(void *arg)
obj->mm.pages->sgl, I915_CACHE_NONE,
true, 0xdeadbeaf, &rq);
if (rq) {
dma_resv_add_excl_fence(obj->base.resv, &rq->fence);
dma_resv_add_fence(obj->base.resv, &rq->fence,
DMA_RESV_USAGE_WRITE);
i915_request_put(rq);
}
+9
View File
@@ -24,4 +24,13 @@ config DRM_INGENIC_IPU
The Image Processing Unit (IPU) will appear as a second primary plane.
config DRM_INGENIC_DW_HDMI
tristate "Ingenic specific support for Synopsys DW HDMI"
depends on MACH_JZ4780
select DRM_DW_HDMI
help
Choose this option to enable Synopsys DesignWare HDMI based driver.
If you want to enable HDMI on Ingenic JZ4780 based SoC, you should
select this option.
endif
+1
View File
@@ -1,3 +1,4 @@
obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o
ingenic-drm-y = ingenic-drm-drv.o
ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o
obj-$(CONFIG_DRM_INGENIC_DW_HDMI) += ingenic-dw-hdmi.o
+27 -1
View File
@@ -833,6 +833,32 @@ static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,
}
}
static u32 *
ingenic_drm_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
{
switch (output_fmt) {
case MEDIA_BUS_FMT_RGB888_1X24:
case MEDIA_BUS_FMT_RGB666_1X18:
case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_RGB888_3X8:
case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
break;
default:
*num_input_fmts = 0;
return NULL;
}
return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state,
crtc_state, conn_state,
output_fmt,
num_input_fmts);
}
static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
{
struct ingenic_drm *priv = drm_device_get_priv(arg);
@@ -984,7 +1010,7 @@ static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts,
};
static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = {
+103
View File
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
* Copyright (C) 2019, 2020 Paul Boddie <paul@boddie.org.uk>
*
* Derived from dw_hdmi-imx.c with i.MX portions removed.
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
static const struct dw_hdmi_mpll_config ingenic_mpll_cfg[] = {
{ 45250000, { { 0x01e0, 0x0000 }, { 0x21e1, 0x0000 }, { 0x41e2, 0x0000 } } },
{ 92500000, { { 0x0140, 0x0005 }, { 0x2141, 0x0005 }, { 0x4142, 0x0005 } } },
{ 148500000, { { 0x00a0, 0x000a }, { 0x20a1, 0x000a }, { 0x40a2, 0x000a } } },
{ 216000000, { { 0x00a0, 0x000a }, { 0x2001, 0x000f }, { 0x4002, 0x000f } } },
{ ~0UL, { { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 } } }
};
static const struct dw_hdmi_curr_ctrl ingenic_cur_ctr[] = {
/*pixelclk bpp8 bpp10 bpp12 */
{ 54000000, { 0x091c, 0x091c, 0x06dc } },
{ 58400000, { 0x091c, 0x06dc, 0x06dc } },
{ 72000000, { 0x06dc, 0x06dc, 0x091c } },
{ 74250000, { 0x06dc, 0x0b5c, 0x091c } },
{ 118800000, { 0x091c, 0x091c, 0x06dc } },
{ 216000000, { 0x06dc, 0x0b5c, 0x091c } },
{ ~0UL, { 0x0000, 0x0000, 0x0000 } },
};
/*
* Resistance term 133Ohm Cfg
* PREEMP config 0.00
* TX/CK level 10
*/
static const struct dw_hdmi_phy_config ingenic_phy_config[] = {
/*pixelclk symbol term vlev */
{ 216000000, 0x800d, 0x0005, 0x01ad},
{ ~0UL, 0x0000, 0x0000, 0x0000}
};
static enum drm_mode_status
ingenic_dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->clock < 13500)
return MODE_CLOCK_LOW;
/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
if (mode->clock > 216000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static struct dw_hdmi_plat_data ingenic_dw_hdmi_plat_data = {
.mpll_cfg = ingenic_mpll_cfg,
.cur_ctr = ingenic_cur_ctr,
.phy_config = ingenic_phy_config,
.mode_valid = ingenic_dw_hdmi_mode_valid,
.output_port = 1,
};
static const struct of_device_id ingenic_dw_hdmi_dt_ids[] = {
{ .compatible = "ingenic,jz4780-dw-hdmi" },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, ingenic_dw_hdmi_dt_ids);
static void ingenic_dw_hdmi_cleanup(void *data)
{
struct dw_hdmi *hdmi = (struct dw_hdmi *)data;
dw_hdmi_remove(hdmi);
}
static int ingenic_dw_hdmi_probe(struct platform_device *pdev)
{
struct dw_hdmi *hdmi;
hdmi = dw_hdmi_probe(pdev, &ingenic_dw_hdmi_plat_data);
if (IS_ERR(hdmi))
return PTR_ERR(hdmi);
return devm_add_action_or_reset(&pdev->dev, ingenic_dw_hdmi_cleanup, hdmi);
}
static struct platform_driver ingenic_dw_hdmi_driver = {
.probe = ingenic_dw_hdmi_probe,
.driver = {
.name = "dw-hdmi-ingenic",
.of_match_table = ingenic_dw_hdmi_dt_ids,
},
};
module_platform_driver(ingenic_dw_hdmi_driver);
MODULE_DESCRIPTION("JZ4780 Specific DW-HDMI Driver Extension");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dw-hdmi-ingenic");
+3 -4
View File
@@ -364,10 +364,9 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
fence = lima_sched_context_queue_task(submit->task);
for (i = 0; i < submit->nr_bos; i++) {
if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE)
dma_resv_add_excl_fence(lima_bo_resv(bos[i]), fence);
else
dma_resv_add_shared_fence(lima_bo_resv(bos[i]), fence);
dma_resv_add_fence(lima_bo_resv(bos[i]), fence,
submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE ?
DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
}
drm_gem_unlock_reservations((struct drm_gem_object **)bos,
+2 -1
View File
@@ -848,7 +848,8 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
long ret;
ret = dma_resv_wait_timeout(obj->resv, write, true, remain);
ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write),
true, remain);
if (ret == 0)
return remain == 0 ? -EBUSY : -ETIMEDOUT;
else if (ret < 0)
+4 -2
View File
@@ -395,9 +395,11 @@ static void submit_attach_object_fences(struct msm_gem_submit *submit)
struct drm_gem_object *obj = &submit->bos[i].obj->base;
if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
dma_resv_add_excl_fence(obj->resv, submit->user_fence);
dma_resv_add_fence(obj->resv, submit->user_fence,
DMA_RESV_USAGE_WRITE);
else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
dma_resv_add_shared_fence(obj->resv, submit->user_fence);
dma_resv_add_fence(obj->resv, submit->user_fence,
DMA_RESV_USAGE_READ);
}
}
+1 -1
View File
@@ -22,7 +22,7 @@
#include "base.h"
#include "atom.h"
const u32
static const u32
base917c_format[] = {
DRM_FORMAT_C8,
DRM_FORMAT_XRGB8888,
+2 -1
View File
@@ -558,7 +558,8 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
asyw->image.handle[0] = ctxdma->object.handle;
}
ret = dma_resv_get_singleton(nvbo->bo.base.resv, false,
ret = dma_resv_get_singleton(nvbo->bo.base.resv,
DMA_RESV_USAGE_WRITE,
&asyw->state.fence);
if (ret)
return ret;
+9 -8
View File
@@ -962,11 +962,11 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
struct dma_fence *fence;
int ret;
/* TODO: This is actually a memory management dependency */
ret = dma_resv_get_singleton(bo->base.resv, false, &fence);
ret = dma_resv_get_singleton(bo->base.resv, DMA_RESV_USAGE_WRITE,
&fence);
if (ret)
dma_resv_wait_timeout(bo->base.resv, false, false,
MAX_SCHEDULE_TIMEOUT);
dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_WRITE,
false, MAX_SCHEDULE_TIMEOUT);
nv10_bo_put_tile_region(dev, *old_tile, fence);
*old_tile = new_tile;
@@ -1308,10 +1308,11 @@ nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool excl
{
struct dma_resv *resv = nvbo->bo.base.resv;
if (exclusive)
dma_resv_add_excl_fence(resv, &fence->base);
else if (fence)
dma_resv_add_shared_fence(resv, &fence->base);
if (!fence)
return;
dma_resv_add_fence(resv, &fence->base, exclusive ?
DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
}
static void
+8 -4
View File
@@ -350,17 +350,21 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
if (ret)
return ret;
/* Waiting for the exclusive fence first causes performance regressions
* under some circumstances. So manually wait for the shared ones first.
/* Waiting for the writes first causes performance regressions
* under some circumstances. So manually wait for the reads first.
*/
for (i = 0; i < 2; ++i) {
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_for_each_fence(&cursor, resv, exclusive, fence) {
dma_resv_for_each_fence(&cursor, resv,
dma_resv_usage_rw(exclusive),
fence) {
enum dma_resv_usage usage;
struct nouveau_fence *f;
if (i == 0 && dma_resv_iter_is_exclusive(&cursor))
usage = dma_resv_iter_usage(&cursor);
if (i == 0 && usage == DMA_RESV_USAGE_WRITE)
continue;
f = nouveau_local_fence(fence, chan->drm);
+2 -1
View File
@@ -962,7 +962,8 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
return -ENOENT;
nvbo = nouveau_gem_object(gem);
lret = dma_resv_wait_timeout(nvbo->bo.base.resv, write, true,
lret = dma_resv_wait_timeout(nvbo->bo.base.resv,
dma_resv_usage_rw(write), true,
no_wait ? 0 : 30 * HZ);
if (!lret)
ret = -EBUSY;
+1 -16
View File
@@ -93,22 +93,7 @@ int nouveau_gem_prime_pin(struct drm_gem_object *obj)
if (ret)
return -EINVAL;
ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
if (ret)
goto error;
if (nvbo->bo.moving)
ret = dma_fence_wait(nvbo->bo.moving, true);
ttm_bo_unreserve(&nvbo->bo);
if (ret)
goto error;
return ret;
error:
nouveau_bo_unpin(nvbo);
return ret;
return 0;
}
void nouveau_gem_prime_unpin(struct drm_gem_object *obj)
@@ -143,7 +143,7 @@ gf108_gr = {
}
};
const struct gf100_gr_fwif
static const struct gf100_gr_fwif
gf108_gr_fwif[] = {
{ -1, gf100_gr_load, &gf108_gr },
{ -1, gf100_gr_nofw, &gf108_gr },
+13 -18
View File
@@ -30,9 +30,9 @@ struct panel_lvds {
const char *label;
unsigned int width;
unsigned int height;
struct videomode video_mode;
struct drm_display_mode dmode;
u32 bus_flags;
unsigned int bus_format;
bool data_mirror;
struct regulator *supply;
@@ -87,21 +87,18 @@ static int panel_lvds_get_modes(struct drm_panel *panel,
struct panel_lvds *lvds = to_panel_lvds(panel);
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev);
mode = drm_mode_duplicate(connector->dev, &lvds->dmode);
if (!mode)
return 0;
drm_display_mode_from_videomode(&lvds->video_mode, mode);
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
connector->display_info.width_mm = lvds->width;
connector->display_info.height_mm = lvds->height;
connector->display_info.width_mm = lvds->dmode.width_mm;
connector->display_info.height_mm = lvds->dmode.height_mm;
drm_display_info_set_bus_formats(&connector->display_info,
&lvds->bus_format, 1);
connector->display_info.bus_flags = lvds->data_mirror
? DRM_BUS_FLAG_DATA_LSB_TO_MSB
: DRM_BUS_FLAG_DATA_MSB_TO_LSB;
connector->display_info.bus_flags = lvds->bus_flags;
drm_connector_set_panel_orientation(connector, lvds->orientation);
return 1;
@@ -116,7 +113,6 @@ static const struct drm_panel_funcs panel_lvds_funcs = {
static int panel_lvds_parse_dt(struct panel_lvds *lvds)
{
struct device_node *np = lvds->dev->of_node;
struct display_timing timing;
int ret;
ret = of_drm_get_panel_orientation(np, &lvds->orientation);
@@ -125,23 +121,20 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
return ret;
}
ret = of_get_display_timing(np, "panel-timing", &timing);
ret = of_get_drm_panel_display_mode(np, &lvds->dmode, &lvds->bus_flags);
if (ret < 0) {
dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",
np, ret);
return ret;
}
videomode_from_timing(&timing, &lvds->video_mode);
ret = of_property_read_u32(np, "width-mm", &lvds->width);
if (ret < 0) {
if (lvds->dmode.width_mm == 0) {
dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",
np, "width-mm");
return -ENODEV;
}
ret = of_property_read_u32(np, "height-mm", &lvds->height);
if (ret < 0) {
if (lvds->dmode.height_mm == 0) {
dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",
np, "height-mm");
return -ENODEV;
@@ -158,7 +151,9 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
lvds->bus_format = ret;
lvds->data_mirror = of_property_read_bool(np, "data-mirror");
lvds->bus_flags |= of_property_read_bool(np, "data-mirror") ?
DRM_BUS_FLAG_DATA_LSB_TO_MSB :
DRM_BUS_FLAG_DATA_MSB_TO_LSB;
return 0;
}
+2 -1
View File
@@ -316,7 +316,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,
if (!gem_obj)
return -ENOENT;
ret = dma_resv_wait_timeout(gem_obj->resv, true, true, timeout);
ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_READ,
true, timeout);
if (!ret)
ret = timeout ? -ETIMEDOUT : -EBUSY;
+1 -1
View File
@@ -268,7 +268,7 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos,
int i;
for (i = 0; i < bo_count; i++)
dma_resv_add_excl_fence(bos[i]->resv, fence);
dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
}
int panfrost_job_push(struct panfrost_job *job)
+2 -1
View File
@@ -61,7 +61,8 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data)
struct dma_fence *fence;
int rel = 0;
dma_resv_iter_begin(&cursor, bo->tbo.base.resv, true);
dma_resv_iter_begin(&cursor, bo->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (dma_resv_iter_is_restarted(&cursor))
rel = 0;
+2 -1
View File
@@ -429,7 +429,8 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)
list_for_each_entry(entry, &release->bos, head) {
bo = entry->bo;
dma_resv_add_shared_fence(bo->base.resv, &release->base);
dma_resv_add_fence(bo->base.resv, &release->base,
DMA_RESV_USAGE_READ);
ttm_bo_move_to_lru_tail_unlocked(bo);
dma_resv_unlock(bo->base.resv);
}
+6 -33
View File
@@ -222,41 +222,14 @@ void qxl_ttm_fini(struct qxl_device *qdev)
DRM_INFO("qxl: ttm finalized\n");
}
#define QXL_DEBUGFS_MEM_TYPES 2
#if defined(CONFIG_DEBUG_FS)
static int qxl_mm_dump_table(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *)m->private;
struct ttm_resource_manager *man = (struct ttm_resource_manager *)node->info_ent->data;
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
#endif
void qxl_ttm_debugfs_init(struct qxl_device *qdev)
{
#if defined(CONFIG_DEBUG_FS)
static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES];
static char qxl_mem_types_names[QXL_DEBUGFS_MEM_TYPES][32];
unsigned int i;
for (i = 0; i < QXL_DEBUGFS_MEM_TYPES; i++) {
if (i == 0)
sprintf(qxl_mem_types_names[i], "qxl_mem_mm");
else
sprintf(qxl_mem_types_names[i], "qxl_surf_mm");
qxl_mem_types_list[i].name = qxl_mem_types_names[i];
qxl_mem_types_list[i].show = &qxl_mm_dump_table;
qxl_mem_types_list[i].driver_features = 0;
if (i == 0)
qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_VRAM);
else
qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_PRIV);
}
qxl_debugfs_add_files(qdev, qxl_mem_types_list, i);
ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev,
TTM_PL_VRAM),
qdev->ddev.primary->debugfs_root, "qxl_mem_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev,
TTM_PL_PRIV),
qdev->ddev.primary->debugfs_root, "qxl_surf_mm");
#endif
}
+2 -1
View File
@@ -533,7 +533,8 @@ static int radeon_crtc_page_flip_target(struct drm_crtc *crtc,
DRM_ERROR("failed to pin new rbo buffer before flip\n");
goto cleanup;
}
r = dma_resv_get_singleton(new_rbo->tbo.base.resv, false, &work->fence);
r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
&work->fence);
if (r) {
radeon_bo_unreserve(new_rbo);
DRM_ERROR("failed to get new rbo buffer fences\n");
+6 -3
View File
@@ -162,7 +162,9 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
}
if (domain == RADEON_GEM_DOMAIN_CPU) {
/* Asking for cpu access wait for object idle */
r = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ);
r = dma_resv_wait_timeout(robj->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP,
true, 30 * HZ);
if (!r)
r = -EBUSY;
@@ -524,7 +526,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
}
robj = gem_to_radeon_bo(gobj);
r = dma_resv_test_signaled(robj->tbo.base.resv, true);
r = dma_resv_test_signaled(robj->tbo.base.resv, DMA_RESV_USAGE_READ);
if (r == 0)
r = -EBUSY;
else
@@ -553,7 +555,8 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
}
robj = gem_to_radeon_bo(gobj);
ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ);
ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ,
true, 30 * HZ);
if (ret == 0)
r = -EBUSY;
else if (ret < 0)
+2 -2
View File
@@ -66,8 +66,8 @@ static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn,
return true;
}
r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
MAX_SCHEDULE_TIMEOUT);
r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP,
false, MAX_SCHEDULE_TIMEOUT);
if (r <= 0)
DRM_ERROR("(%ld) failed to wait for user bo\n", r);
+8 -5
View File
@@ -219,7 +219,12 @@ int radeon_bo_create(struct radeon_device *rdev,
int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)
{
bool is_iomem;
int r;
long r;
r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL,
false, MAX_SCHEDULE_TIMEOUT);
if (r < 0)
return r;
if (bo->kptr) {
if (ptr) {
@@ -791,8 +796,6 @@ void radeon_bo_fence(struct radeon_bo *bo, struct radeon_fence *fence,
return;
}
if (shared)
dma_resv_add_shared_fence(resv, &fence->base);
else
dma_resv_add_excl_fence(resv, &fence->base);
dma_resv_add_fence(resv, &fence->base, shared ?
DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE);
}
+2 -12
View File
@@ -77,19 +77,9 @@ int radeon_gem_prime_pin(struct drm_gem_object *obj)
/* pin buffer into GTT */
ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL);
if (unlikely(ret))
goto error;
if (likely(ret == 0))
bo->prime_shared_count++;
if (bo->tbo.moving) {
ret = dma_fence_wait(bo->tbo.moving, false);
if (unlikely(ret)) {
radeon_bo_unpin(bo);
goto error;
}
}
bo->prime_shared_count++;
error:
radeon_bo_unreserve(bo);
return ret;
}
+1 -1
View File
@@ -96,7 +96,7 @@ int radeon_sync_resv(struct radeon_device *rdev,
struct dma_fence *f;
int r = 0;
dma_resv_for_each_fence(&cursor, resv, shared, f) {
dma_resv_for_each_fence(&cursor, resv, dma_resv_usage_rw(shared), f) {
fence = to_radeon_fence(f);
if (fence && fence->rdev == rdev)
radeon_sync_fence(sync, fence);
+6 -30
View File
@@ -781,17 +781,6 @@ void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size)
#if defined(CONFIG_DEBUG_FS)
static int radeon_mm_vram_dump_table_show(struct seq_file *m, void *unused)
{
struct radeon_device *rdev = (struct radeon_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev,
TTM_PL_VRAM);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)
{
struct radeon_device *rdev = (struct radeon_device *)m->private;
@@ -799,19 +788,6 @@ static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)
return ttm_pool_debugfs(&rdev->mman.bdev.pool, m);
}
static int radeon_mm_gtt_dump_table_show(struct seq_file *m, void *unused)
{
struct radeon_device *rdev = (struct radeon_device *)m->private;
struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev,
TTM_PL_TT);
struct drm_printer p = drm_seq_file_printer(m);
ttm_resource_manager_debug(man, &p);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(radeon_mm_vram_dump_table);
DEFINE_SHOW_ATTRIBUTE(radeon_mm_gtt_dump_table);
DEFINE_SHOW_ATTRIBUTE(radeon_ttm_page_pool);
static int radeon_ttm_vram_open(struct inode *inode, struct file *filep)
@@ -930,15 +906,15 @@ static void radeon_ttm_debugfs_init(struct radeon_device *rdev)
debugfs_create_file("radeon_vram", 0444, root, rdev,
&radeon_ttm_vram_fops);
debugfs_create_file("radeon_gtt", 0444, root, rdev,
&radeon_ttm_gtt_fops);
debugfs_create_file("radeon_vram_mm", 0444, root, rdev,
&radeon_mm_vram_dump_table_fops);
debugfs_create_file("radeon_gtt_mm", 0444, root, rdev,
&radeon_mm_gtt_dump_table_fops);
debugfs_create_file("ttm_page_pool", 0444, root, rdev,
&radeon_ttm_page_pool_fops);
ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
TTM_PL_VRAM),
root, "radeon_vram_mm");
ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
TTM_PL_TT),
root, "radeon_gtt_mm");
#endif
}

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