mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
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:
@@ -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 = <<9211_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,.*":
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
89
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
Normal file
89
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
Normal file
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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, ®16, 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
drivers/gpu/drm/bridge/lontium-lt9211.c
Normal file
802
drivers/gpu/drm/bridge/lontium-lt9211.c
Normal 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 = <9211_rw_table,
|
||||
.wr_table = <9211_rw_table,
|
||||
.volatile_table = <9211_rw_table,
|
||||
.ranges = <9211_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, <9211_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 = <9211_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");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
199
drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
Normal file
199
drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
Normal file
@@ -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);
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c
Normal file
103
drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c
Normal 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");
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "base.h"
|
||||
#include "atom.h"
|
||||
|
||||
const u32
|
||||
static const u32
|
||||
base917c_format[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user