mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
Merge tag 'drm-misc-next-2024-04-25' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.10-rc1: UAPI Changes: Cross-subsystem Changes: - Devicetree updates for rockchip (#sound-dai-cells) - Add dt bindings for new panels. - Change bridge/tc358775 dt bindings. Core Changes: - Fix SIZE_HINTS cursor property doc. - Parse topology blocks for all DispID < 2.0. - Implement support for tracking cleared free memory, use it in amdgpu. - Drop seq_file.h from drm_print.h, and include debugfs.h explicitly where needed (drivers). Driver Changes: - Small fixes to rockchip, panthor, v3d, bridge chaining, xlx. - Add Khadas TS050 V2, EDO RM69380 OLED, CSOT MNB601LS1-1 panels, - Add SAM9X7 SoC's LVDS controller. - More driver conversions to struct drm_edid. - Support tc358765 in tc358775 bridge. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/1ab99848-8fb8-41a6-8967-c4ce6f3634fd@linux.intel.com
This commit is contained in:
commit
90153b3666
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/microchip,sam9x75-lvds.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip SAM9X75 LVDS Controller
|
||||
|
||||
maintainers:
|
||||
- Dharma Balasubiramani <dharma.b@microchip.com>
|
||||
|
||||
description:
|
||||
The Low Voltage Differential Signaling Controller (LVDSC) manages data
|
||||
format conversion from the LCD Controller internal DPI bus to OpenLDI
|
||||
LVDS output signals. LVDSC functions include bit mapping, balanced mode
|
||||
management, and serializer.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,sam9x75-lvds
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Peripheral Bus Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
lvds-controller@f8060000 {
|
||||
compatible = "microchip,sam9x75-lvds";
|
||||
reg = <0xf8060000 0x100>;
|
||||
interrupts = <56 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&pmc PMC_TYPE_PERIPHERAL 56>;
|
||||
clock-names = "pclk";
|
||||
};
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- Vinay Simha BN <simhavcs@gmail.com>
|
||||
|
||||
description: |
|
||||
This binding supports DSI to LVDS bridge TC358775
|
||||
This binding supports DSI to LVDS bridges TC358765 and TC358775
|
||||
|
||||
MIPI DSI-RX Data 4-lane, CLK 1-lane with data rates up to 800 Mbps/lane.
|
||||
Video frame size:
|
||||
@ -21,7 +21,9 @@ description: |
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: toshiba,tc358775
|
||||
enum:
|
||||
- toshiba,tc358765
|
||||
- toshiba,tc358775
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -46,11 +48,27 @@ properties:
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
description: |
|
||||
DSI Input. The remote endpoint phandle should be a
|
||||
reference to a valid mipi_dsi_host device node.
|
||||
|
||||
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: |
|
||||
@ -70,10 +88,19 @@ required:
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
- stby-gpios
|
||||
- reset-gpios
|
||||
- ports
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: toshiba,tc358765
|
||||
then:
|
||||
properties:
|
||||
stby-gpios: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
@ -108,6 +135,7 @@ examples:
|
||||
reg = <0>;
|
||||
d2l_in_test: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
data-lanes = <1 2 3 4>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -132,7 +160,6 @@ examples:
|
||||
reg = <1>;
|
||||
dsi0_out: endpoint {
|
||||
remote-endpoint = <&d2l_in_test>;
|
||||
data-lanes = <0 1 2 3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -167,6 +194,7 @@ examples:
|
||||
reg = <0>;
|
||||
d2l_in_dual: endpoint {
|
||||
remote-endpoint = <&dsi0_out_dual>;
|
||||
data-lanes = <1 2 3 4>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -198,7 +226,6 @@ examples:
|
||||
reg = <1>;
|
||||
dsi0_out_dual: endpoint {
|
||||
remote-endpoint = <&d2l_in_dual>;
|
||||
data-lanes = <0 1 2 3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -36,6 +36,8 @@ properties:
|
||||
- jdi,fhd-r63452
|
||||
# Khadas TS050 5" 1080x1920 LCD panel
|
||||
- khadas,ts050
|
||||
# Khadas TS050 V2 5" 1080x1920 LCD panel
|
||||
- khadas,ts050v2
|
||||
# Kingdisplay KD097D04 9.7" 1536x2048 TFT LCD panel
|
||||
- kingdisplay,kd097d04
|
||||
# LG ACX467AKM-7 4.95" 1080×1920 LCD Panel
|
||||
|
@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/raydium,rm69380.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Raydium RM69380-based DSI display panels
|
||||
|
||||
maintainers:
|
||||
- David Wronek <david@mainlining.org>
|
||||
|
||||
description:
|
||||
The Raydium RM69380 is a generic DSI panel IC used to control
|
||||
OLED panels.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common-dual.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- lenovo,j716f-edo-rm69380
|
||||
- const: raydium,rm69380
|
||||
description: This indicates the panel manufacturer of the panel
|
||||
that is in turn using the RM69380 panel driver. The compatible
|
||||
string determines how the RM69380 panel driver shall be configured
|
||||
to work with the indicated panel. The raydium,rm69380 compatible shall
|
||||
always be provided as a fallback.
|
||||
|
||||
avdd-supply:
|
||||
description: Analog voltage rail
|
||||
|
||||
vddio-supply:
|
||||
description: I/O voltage rail
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description: phandle of gpio for reset line - This should be active low
|
||||
|
||||
reg: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- vddio-supply
|
||||
- reset-gpios
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "lenovo,j716f-edo-rm69380", "raydium,rm69380";
|
||||
reg = <0>;
|
||||
|
||||
avdd-supply = <&panel_avdd_regulator>;
|
||||
vddio-supply = <&vreg_l14a>;
|
||||
reset-gpios = <&tlmm 75 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
panel_in_0: endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
panel_in_1: endpoint {
|
||||
remote-endpoint = <&mdss_dsi1_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -15,6 +15,7 @@ description: |
|
||||
|
||||
allOf:
|
||||
- $ref: ../bridge/synopsys,dw-hdmi.yaml#
|
||||
- $ref: /schemas/sound/dai-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@ -124,6 +125,9 @@ properties:
|
||||
description:
|
||||
phandle to the GRF to mux vopl/vopb.
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -153,6 +157,7 @@ examples:
|
||||
ddc-i2c-bus = <&i2c5>;
|
||||
power-domains = <&power RK3288_PD_VIO>;
|
||||
rockchip,grf = <&grf>;
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
@ -37,6 +37,9 @@ properties:
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
@ -66,6 +69,7 @@ required:
|
||||
- ports
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/sound/dai-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -106,6 +110,7 @@ examples:
|
||||
clock-names = "pclk";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&hdmi_ctl>;
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
@ -10,6 +10,9 @@ maintainers:
|
||||
- Sandy Huang <hjc@rock-chips.com>
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/sound/dai-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rockchip,rk3066-hdmi
|
||||
@ -34,6 +37,9 @@ properties:
|
||||
description:
|
||||
This soc uses GRF regs to switch the HDMI TX input between vop0 and vop1.
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
@ -83,6 +89,7 @@ examples:
|
||||
pinctrl-names = "default";
|
||||
power-domains = <&power RK3066_PD_VIO>;
|
||||
rockchip,grf = <&grf>;
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
@ -14582,6 +14582,14 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml
|
||||
F: drivers/pwm/pwm-atmel.c
|
||||
|
||||
MICROCHIP SAM9x7-COMPATIBLE LVDS CONTROLLER
|
||||
M: Manikandan Muralidharan <manikandan.m@microchip.com>
|
||||
M: Dharma Balasubiramani <dharma.b@microchip.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-lvds.yaml
|
||||
F: drivers/gpu/drm/bridge/microchip-lvds.c
|
||||
|
||||
MICROCHIP SAMA5D2-COMPATIBLE ADC DRIVER
|
||||
M: Eugen Hristev <eugen.hristev@microchip.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Copyright (C) 2020-2023 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_trace.h"
|
||||
#include "amdgpu_amdkfd.h"
|
||||
#include "amdgpu_vram_mgr.h"
|
||||
|
||||
/**
|
||||
* DOC: amdgpu_object
|
||||
@ -601,8 +602,7 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
|
||||
if (!amdgpu_bo_support_uswc(bo->flags))
|
||||
bo->flags &= ~AMDGPU_GEM_CREATE_CPU_GTT_USWC;
|
||||
|
||||
if (adev->ras_enabled)
|
||||
bo->flags |= AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE;
|
||||
bo->flags |= AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE;
|
||||
|
||||
bo->tbo.bdev = &adev->mman.bdev;
|
||||
if (bp->domain & (AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA |
|
||||
@ -633,7 +633,7 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
|
||||
bo->tbo.resource->mem_type == TTM_PL_VRAM) {
|
||||
struct dma_fence *fence;
|
||||
|
||||
r = amdgpu_fill_buffer(bo, 0, bo->tbo.base.resv, &fence, true);
|
||||
r = amdgpu_ttm_clear_buffer(bo, bo->tbo.base.resv, &fence);
|
||||
if (unlikely(r))
|
||||
goto fail_unreserve;
|
||||
|
||||
@ -1366,8 +1366,9 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo)
|
||||
if (WARN_ON_ONCE(!dma_resv_trylock(bo->base.resv)))
|
||||
return;
|
||||
|
||||
r = amdgpu_fill_buffer(abo, AMDGPU_POISON, bo->base.resv, &fence, true);
|
||||
r = amdgpu_fill_buffer(abo, 0, bo->base.resv, &fence, true);
|
||||
if (!WARN_ON(r)) {
|
||||
amdgpu_vram_mgr_set_cleared(bo->resource);
|
||||
amdgpu_bo_fence(abo, fence, false);
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
|
@ -164,4 +164,29 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_res_cleared - check if blocks are cleared
|
||||
*
|
||||
* @cur: the cursor to extract the block
|
||||
*
|
||||
* Check if the @cur block is cleared
|
||||
*/
|
||||
static inline bool amdgpu_res_cleared(struct amdgpu_res_cursor *cur)
|
||||
{
|
||||
struct drm_buddy_block *block;
|
||||
|
||||
switch (cur->mem_type) {
|
||||
case TTM_PL_VRAM:
|
||||
block = cur->node;
|
||||
|
||||
if (!amdgpu_vram_mgr_is_cleared(block))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -378,11 +378,12 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo,
|
||||
(abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE)) {
|
||||
struct dma_fence *wipe_fence = NULL;
|
||||
|
||||
r = amdgpu_fill_buffer(abo, AMDGPU_POISON, NULL, &wipe_fence,
|
||||
false);
|
||||
r = amdgpu_fill_buffer(abo, 0, NULL, &wipe_fence,
|
||||
false);
|
||||
if (r) {
|
||||
goto error;
|
||||
} else if (wipe_fence) {
|
||||
amdgpu_vram_mgr_set_cleared(bo->resource);
|
||||
dma_fence_put(fence);
|
||||
fence = wipe_fence;
|
||||
}
|
||||
@ -2226,6 +2227,71 @@ static int amdgpu_ttm_fill_mem(struct amdgpu_ring *ring, uint32_t src_data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_ttm_clear_buffer - clear memory buffers
|
||||
* @bo: amdgpu buffer object
|
||||
* @resv: reservation object
|
||||
* @fence: dma_fence associated with the operation
|
||||
*
|
||||
* Clear the memory buffer resource.
|
||||
*
|
||||
* Returns:
|
||||
* 0 for success or a negative error code on failure.
|
||||
*/
|
||||
int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo,
|
||||
struct dma_resv *resv,
|
||||
struct dma_fence **fence)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
|
||||
struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
|
||||
struct amdgpu_res_cursor cursor;
|
||||
u64 addr;
|
||||
int r;
|
||||
|
||||
if (!adev->mman.buffer_funcs_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (!fence)
|
||||
return -EINVAL;
|
||||
|
||||
*fence = dma_fence_get_stub();
|
||||
|
||||
amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &cursor);
|
||||
|
||||
mutex_lock(&adev->mman.gtt_window_lock);
|
||||
while (cursor.remaining) {
|
||||
struct dma_fence *next = NULL;
|
||||
u64 size;
|
||||
|
||||
if (amdgpu_res_cleared(&cursor)) {
|
||||
amdgpu_res_next(&cursor, cursor.size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Never clear more than 256MiB at once to avoid timeouts */
|
||||
size = min(cursor.size, 256ULL << 20);
|
||||
|
||||
r = amdgpu_ttm_map_buffer(&bo->tbo, bo->tbo.resource, &cursor,
|
||||
1, ring, false, &size, &addr);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
r = amdgpu_ttm_fill_mem(ring, 0, addr, size, resv,
|
||||
&next, true, true);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
dma_fence_put(*fence);
|
||||
*fence = next;
|
||||
|
||||
amdgpu_res_next(&cursor, size);
|
||||
}
|
||||
err:
|
||||
mutex_unlock(&adev->mman.gtt_window_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int amdgpu_fill_buffer(struct amdgpu_bo *bo,
|
||||
uint32_t src_data,
|
||||
struct dma_resv *resv,
|
||||
|
@ -38,8 +38,6 @@
|
||||
#define AMDGPU_GTT_MAX_TRANSFER_SIZE 512
|
||||
#define AMDGPU_GTT_NUM_TRANSFER_WINDOWS 2
|
||||
|
||||
#define AMDGPU_POISON 0xd0bed0be
|
||||
|
||||
extern const struct attribute_group amdgpu_vram_mgr_attr_group;
|
||||
extern const struct attribute_group amdgpu_gtt_mgr_attr_group;
|
||||
|
||||
@ -158,6 +156,9 @@ int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
|
||||
uint64_t size, bool tmz,
|
||||
struct dma_resv *resv,
|
||||
struct dma_fence **f);
|
||||
int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo,
|
||||
struct dma_resv *resv,
|
||||
struct dma_fence **fence);
|
||||
int amdgpu_fill_buffer(struct amdgpu_bo *bo,
|
||||
uint32_t src_data,
|
||||
struct dma_resv *resv,
|
||||
|
@ -450,6 +450,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
|
||||
{
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
struct amdgpu_device *adev = to_amdgpu_device(mgr);
|
||||
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo);
|
||||
u64 vis_usage = 0, max_bytes, min_block_size;
|
||||
struct amdgpu_vram_mgr_resource *vres;
|
||||
u64 size, remaining_size, lpfn, fpfn;
|
||||
@ -501,6 +502,9 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
|
||||
if (place->flags & TTM_PL_FLAG_CONTIGUOUS)
|
||||
vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;
|
||||
|
||||
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CLEARED)
|
||||
vres->flags |= DRM_BUDDY_CLEAR_ALLOCATION;
|
||||
|
||||
if (fpfn || lpfn != mgr->mm.size)
|
||||
/* Allocate blocks in desired range */
|
||||
vres->flags |= DRM_BUDDY_RANGE_ALLOCATION;
|
||||
@ -571,7 +575,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
|
||||
return 0;
|
||||
|
||||
error_free_blocks:
|
||||
drm_buddy_free_list(mm, &vres->blocks);
|
||||
drm_buddy_free_list(mm, &vres->blocks, 0);
|
||||
mutex_unlock(&mgr->lock);
|
||||
error_fini:
|
||||
ttm_resource_fini(man, &vres->base);
|
||||
@ -604,7 +608,7 @@ static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
|
||||
|
||||
amdgpu_vram_mgr_do_reserve(man);
|
||||
|
||||
drm_buddy_free_list(mm, &vres->blocks);
|
||||
drm_buddy_free_list(mm, &vres->blocks, vres->flags);
|
||||
mutex_unlock(&mgr->lock);
|
||||
|
||||
atomic64_sub(vis_usage, &mgr->vis_usage);
|
||||
@ -912,7 +916,7 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
|
||||
kfree(rsv);
|
||||
|
||||
list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) {
|
||||
drm_buddy_free_list(&mgr->mm, &rsv->allocated);
|
||||
drm_buddy_free_list(&mgr->mm, &rsv->allocated, 0);
|
||||
kfree(rsv);
|
||||
}
|
||||
if (!adev->gmc.is_app_apu)
|
||||
|
@ -53,10 +53,20 @@ static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block)
|
||||
return (u64)PAGE_SIZE << drm_buddy_block_order(block);
|
||||
}
|
||||
|
||||
static inline bool amdgpu_vram_mgr_is_cleared(struct drm_buddy_block *block)
|
||||
{
|
||||
return drm_buddy_block_is_clear(block);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline void amdgpu_vram_mgr_set_cleared(struct ttm_resource *res)
|
||||
{
|
||||
to_amdgpu_vram_mgr_resource(res)->flags |= DRM_BUDDY_CLEARED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -189,6 +189,13 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
|
||||
to DP++. This is used with the i.MX6 imx-ldb
|
||||
driver. You are likely to say N here.
|
||||
|
||||
config DRM_MICROCHIP_LVDS_SERIALIZER
|
||||
tristate "Microchip LVDS serializer support"
|
||||
depends on OF
|
||||
depends on DRM_ATMEL_HLCDC
|
||||
help
|
||||
Support for Microchip's LVDS serializer.
|
||||
|
||||
config DRM_NWL_MIPI_DSI
|
||||
tristate "Northwest Logic MIPI DSI Host controller"
|
||||
depends on DRM
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
|
||||
obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o
|
||||
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
|
||||
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
|
||||
obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o
|
||||
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
|
||||
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
|
||||
obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
|
||||
|
@ -356,6 +356,7 @@ struct adv7511 {
|
||||
enum drm_connector_status status;
|
||||
bool powered;
|
||||
|
||||
struct drm_bridge *next_bridge;
|
||||
struct drm_display_mode curr_mode;
|
||||
|
||||
unsigned int f_tmds;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
@ -951,6 +952,12 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge,
|
||||
struct adv7511 *adv = bridge_to_adv7511(bridge);
|
||||
int ret = 0;
|
||||
|
||||
if (adv->next_bridge) {
|
||||
ret = drm_bridge_attach(bridge->encoder, adv->next_bridge, bridge, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
|
||||
ret = adv7511_connector_init(adv);
|
||||
if (ret < 0)
|
||||
@ -1221,6 +1228,11 @@ static int adv7511_probe(struct i2c_client *i2c)
|
||||
|
||||
memset(&link_config, 0, sizeof(link_config));
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL,
|
||||
&adv7511->next_bridge);
|
||||
if (ret && ret != -ENODEV)
|
||||
return ret;
|
||||
|
||||
if (adv7511->info->link_config)
|
||||
ret = adv7511_parse_dt(dev->of_node, &link_config);
|
||||
else
|
||||
|
@ -781,7 +781,6 @@ static struct mipi_dsi_driver chipone_dsi_driver = {
|
||||
.remove = chipone_dsi_remove,
|
||||
.driver = {
|
||||
.name = "chipone-icn6211",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = chipone_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -8,8 +8,8 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE
|
||||
depends on COMMON_CLK
|
||||
depends on DRM_DW_HDMI
|
||||
depends on OF
|
||||
select DRM_IMX8MP_HDMI_PVI
|
||||
select PHY_FSL_SAMSUNG_HDMI_PHY
|
||||
imply DRM_IMX8MP_HDMI_PVI
|
||||
imply PHY_FSL_SAMSUNG_HDMI_PHY
|
||||
help
|
||||
Choose this to enable support for the internal HDMI encoder found
|
||||
on the i.MX8MP SoC.
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
|
229
drivers/gpu/drm/bridge/microchip-lvds.c
Normal file
229
drivers/gpu/drm/bridge/microchip-lvds.c
Normal file
@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Manikandan Muralidharan <manikandan.m@microchip.com>
|
||||
* Author: Dharma Balasubiramani <dharma.b@microchip.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
#define LVDS_POLL_TIMEOUT_MS 1000
|
||||
|
||||
/* LVDSC register offsets */
|
||||
#define LVDSC_CR 0x00
|
||||
#define LVDSC_CFGR 0x04
|
||||
#define LVDSC_SR 0x0C
|
||||
#define LVDSC_WPMR 0xE4
|
||||
|
||||
/* Bitfields in LVDSC_CR (Control Register) */
|
||||
#define LVDSC_CR_SER_EN BIT(0)
|
||||
|
||||
/* Bitfields in LVDSC_CFGR (Configuration Register) */
|
||||
#define LVDSC_CFGR_PIXSIZE_24BITS 0
|
||||
#define LVDSC_CFGR_DEN_POL_HIGH 0
|
||||
#define LVDSC_CFGR_DC_UNBALANCED 0
|
||||
#define LVDSC_CFGR_MAPPING_JEIDA BIT(6)
|
||||
|
||||
/*Bitfields in LVDSC_SR */
|
||||
#define LVDSC_SR_CS BIT(0)
|
||||
|
||||
/* Bitfields in LVDSC_WPMR (Write Protection Mode Register) */
|
||||
#define LVDSC_WPMR_WPKEY_MASK GENMASK(31, 8)
|
||||
#define LVDSC_WPMR_WPKEY_PSSWD 0x4C5644
|
||||
|
||||
struct mchp_lvds {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *pclk;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *panel_bridge;
|
||||
};
|
||||
|
||||
static inline struct mchp_lvds *bridge_to_lvds(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct mchp_lvds, bridge);
|
||||
}
|
||||
|
||||
static inline u32 lvds_readl(struct mchp_lvds *lvds, u32 offset)
|
||||
{
|
||||
return readl_relaxed(lvds->regs + offset);
|
||||
}
|
||||
|
||||
static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val)
|
||||
{
|
||||
writel_relaxed(val, lvds->regs + offset);
|
||||
}
|
||||
|
||||
static void lvds_serialiser_on(struct mchp_lvds *lvds)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS);
|
||||
|
||||
/* The LVDSC registers can only be written if WPEN is cleared */
|
||||
lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD &
|
||||
LVDSC_WPMR_WPKEY_MASK));
|
||||
|
||||
/* Wait for the status of configuration registers to be changed */
|
||||
while (lvds_readl(lvds, LVDSC_SR) & LVDSC_SR_CS) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(lvds->dev, "%s: timeout error\n", __func__);
|
||||
return;
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
/* Configure the LVDSC */
|
||||
lvds_writel(lvds, LVDSC_CFGR, (LVDSC_CFGR_MAPPING_JEIDA |
|
||||
LVDSC_CFGR_DC_UNBALANCED |
|
||||
LVDSC_CFGR_DEN_POL_HIGH |
|
||||
LVDSC_CFGR_PIXSIZE_24BITS));
|
||||
|
||||
/* Enable the LVDS serializer */
|
||||
lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN);
|
||||
}
|
||||
|
||||
static int mchp_lvds_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct mchp_lvds *lvds = bridge_to_lvds(bridge);
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, lvds->panel_bridge,
|
||||
bridge, flags);
|
||||
}
|
||||
|
||||
static void mchp_lvds_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct mchp_lvds *lvds = bridge_to_lvds(bridge);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(lvds->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(lvds->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "failed to get pm runtime: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
lvds_serialiser_on(lvds);
|
||||
}
|
||||
|
||||
static void mchp_lvds_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct mchp_lvds *lvds = bridge_to_lvds(bridge);
|
||||
|
||||
pm_runtime_put(lvds->dev);
|
||||
clk_disable_unprepare(lvds->pclk);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = {
|
||||
.attach = mchp_lvds_attach,
|
||||
.enable = mchp_lvds_enable,
|
||||
.disable = mchp_lvds_disable,
|
||||
};
|
||||
|
||||
static int mchp_lvds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mchp_lvds *lvds;
|
||||
struct device_node *port;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
|
||||
lvds->dev = dev;
|
||||
|
||||
lvds->regs = devm_ioremap_resource(lvds->dev,
|
||||
platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||
if (IS_ERR(lvds->regs))
|
||||
return PTR_ERR(lvds->regs);
|
||||
|
||||
lvds->pclk = devm_clk_get(lvds->dev, "pclk");
|
||||
if (IS_ERR(lvds->pclk))
|
||||
return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk),
|
||||
"could not get pclk_lvds\n");
|
||||
|
||||
port = of_graph_get_remote_node(dev->of_node, 1, 0);
|
||||
if (!port) {
|
||||
dev_err(dev,
|
||||
"can't find port point, please init lvds panel port!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
lvds->panel = of_drm_find_panel(port);
|
||||
of_node_put(port);
|
||||
|
||||
if (IS_ERR(lvds->panel))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
lvds->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
|
||||
|
||||
if (IS_ERR(lvds->panel_bridge))
|
||||
return PTR_ERR(lvds->panel_bridge);
|
||||
|
||||
lvds->bridge.of_node = dev->of_node;
|
||||
lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS;
|
||||
lvds->bridge.funcs = &mchp_lvds_bridge_funcs;
|
||||
|
||||
dev_set_drvdata(dev, lvds);
|
||||
ret = devm_pm_runtime_enable(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "failed to enable pm runtime: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_bridge_add(&lvds->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mchp_lvds_dt_ids[] = {
|
||||
{
|
||||
.compatible = "microchip,sam9x75-lvds",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_lvds_dt_ids);
|
||||
|
||||
static struct platform_driver mchp_lvds_driver = {
|
||||
.probe = mchp_lvds_probe,
|
||||
.driver = {
|
||||
.name = "microchip-lvds",
|
||||
.of_match_table = mchp_lvds_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mchp_lvds_driver);
|
||||
|
||||
MODULE_AUTHOR("Manikandan Muralidharan <manikandan.m@microchip.com>");
|
||||
MODULE_AUTHOR("Dharma Balasubiramani <dharma.b@microchip.com>");
|
||||
MODULE_DESCRIPTION("Low Voltage Differential Signaling Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -4,6 +4,8 @@
|
||||
* Copyright (C) 2017 Broadcom
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
|
@ -401,7 +401,6 @@ static struct mipi_dsi_driver tc358764_driver = {
|
||||
.remove = tc358764_remove,
|
||||
.driver = {
|
||||
.name = "tc358764",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tc358764_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -107,6 +108,7 @@
|
||||
#define RDPKTLN 0x0404 /* Command Read Packet Length */
|
||||
|
||||
#define VPCTRL 0x0450 /* Video Path Control */
|
||||
#define EVTMODE BIT(5) /* Video event mode enable, tc35876x only */
|
||||
#define HTIM1 0x0454 /* Horizontal Timing Control 1 */
|
||||
#define HTIM2 0x0458 /* Horizontal Timing Control 2 */
|
||||
#define VTIM1 0x045C /* Vertical Timing Control 1 */
|
||||
@ -254,6 +256,11 @@ enum tc358775_ports {
|
||||
TC358775_LVDS_OUT1,
|
||||
};
|
||||
|
||||
enum tc3587x5_type {
|
||||
TC358765 = 0x65,
|
||||
TC358775 = 0x75,
|
||||
};
|
||||
|
||||
struct tc_data {
|
||||
struct i2c_client *i2c;
|
||||
struct device *dev;
|
||||
@ -271,6 +278,8 @@ struct tc_data {
|
||||
struct gpio_desc *stby_gpio;
|
||||
u8 lvds_link; /* single-link or dual-link */
|
||||
u8 bpc;
|
||||
|
||||
enum tc3587x5_type type;
|
||||
};
|
||||
|
||||
static inline struct tc_data *bridge_to_tc(struct drm_bridge *b)
|
||||
@ -424,10 +433,16 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
|
||||
d2l_write(tc->i2c, PPI_STARTPPI, PPI_START_FUNCTION);
|
||||
d2l_write(tc->i2c, DSI_STARTDSI, DSI_RX_START);
|
||||
|
||||
/* Video event mode vs pulse mode bit, does not exist for tc358775 */
|
||||
if (tc->type == TC358765)
|
||||
val = EVTMODE;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
if (tc->bpc == 8)
|
||||
val = TC358775_VPCTRL_OPXLFMT(1);
|
||||
val |= TC358775_VPCTRL_OPXLFMT(1);
|
||||
else /* bpc = 6; */
|
||||
val = TC358775_VPCTRL_MSF(1);
|
||||
val |= TC358775_VPCTRL_MSF(1);
|
||||
|
||||
dsiclk = mode->crtc_clock * 3 * tc->bpc / tc->num_dsi_lanes / 1000;
|
||||
clkdiv = dsiclk / (tc->lvds_link == DUAL_LINK ? DIVIDE_BY_6 : DIVIDE_BY_3);
|
||||
@ -454,10 +469,6 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
|
||||
dev_dbg(tc->dev, "bus_formats %04x bpc %d\n",
|
||||
connector->display_info.bus_formats[0],
|
||||
tc->bpc);
|
||||
/*
|
||||
* Default hardware register settings of tc358775 configured
|
||||
* with MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA jeida-24 format
|
||||
*/
|
||||
if (connector->display_info.bus_formats[0] ==
|
||||
MEDIA_BUS_FMT_RGB888_1X7X4_SPWG) {
|
||||
/* VESA-24 */
|
||||
@ -468,14 +479,15 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
|
||||
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_B6, LVI_B7, LVI_B1, LVI_B2));
|
||||
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R6));
|
||||
} else { /* MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - JEIDA-18 */
|
||||
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
|
||||
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R4, LVI_L0, LVI_R5, LVI_G0));
|
||||
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_L0, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
|
||||
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_L0, LVI_L0, LVI_B1, LVI_B2));
|
||||
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_L0));
|
||||
} else {
|
||||
/* JEIDA-18 and JEIDA-24 */
|
||||
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R2, LVI_R3, LVI_R4, LVI_R5));
|
||||
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R6, LVI_R1, LVI_R7, LVI_G2));
|
||||
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G3, LVI_G4, LVI_G0, LVI_G1));
|
||||
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G5, LVI_G6, LVI_G7, LVI_B2));
|
||||
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_B0, LVI_B1, LVI_B3, LVI_B4));
|
||||
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B5, LVI_B6, LVI_B7, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R0));
|
||||
}
|
||||
|
||||
d2l_write(tc->i2c, VFUEN, VFUEN_EN);
|
||||
@ -528,27 +540,24 @@ tc_mode_valid(struct drm_bridge *bridge,
|
||||
static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc)
|
||||
{
|
||||
struct device_node *endpoint;
|
||||
struct device_node *parent;
|
||||
struct device_node *remote;
|
||||
int dsi_lanes = -1;
|
||||
|
||||
/*
|
||||
* To get the data-lanes of dsi, we need to access the dsi0_out of port1
|
||||
* of dsi0 endpoint from bridge port0 of d2l_in
|
||||
*/
|
||||
endpoint = of_graph_get_endpoint_by_regs(tc->dev->of_node,
|
||||
TC358775_DSI_IN, -1);
|
||||
if (endpoint) {
|
||||
/* dsi0_out node */
|
||||
parent = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (parent) {
|
||||
/* dsi0 port 1 */
|
||||
dsi_lanes = drm_of_get_data_lanes_count_ep(parent, 1, -1, 1, 4);
|
||||
of_node_put(parent);
|
||||
}
|
||||
dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4);
|
||||
|
||||
/* Quirk old dtb: Use data lanes from the DSI host side instead of bridge */
|
||||
if (dsi_lanes == -EINVAL || dsi_lanes == -ENODEV) {
|
||||
remote = of_graph_get_remote_endpoint(endpoint);
|
||||
dsi_lanes = drm_of_get_data_lanes_count(remote, 1, 4);
|
||||
of_node_put(remote);
|
||||
if (dsi_lanes >= 1)
|
||||
dev_warn(tc->dev, "no dsi-lanes for the bridge, using host lanes\n");
|
||||
}
|
||||
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (dsi_lanes < 0)
|
||||
return dsi_lanes;
|
||||
|
||||
@ -623,7 +632,21 @@ static int tc_attach_host(struct tc_data *tc)
|
||||
|
||||
dsi->lanes = tc->num_dsi_lanes;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
|
||||
/*
|
||||
* The hs_rate and lp_rate are data rate values. The HS mode is
|
||||
* differential, while the LP mode is single ended. As the HS mode
|
||||
* uses DDR, the DSI clock frequency is half the hs_rate. The 10 Mbs
|
||||
* data rate for LP mode is not specified in the bridge data sheet,
|
||||
* but seems to be part of the MIPI DSI spec.
|
||||
*/
|
||||
if (tc->type == TC358765)
|
||||
dsi->hs_rate = 800000000;
|
||||
else
|
||||
dsi->hs_rate = 1000000000;
|
||||
dsi->lp_rate = 10000000;
|
||||
|
||||
ret = devm_mipi_dsi_attach(dev, dsi);
|
||||
if (ret < 0) {
|
||||
@ -646,6 +669,7 @@ static int tc_probe(struct i2c_client *client)
|
||||
|
||||
tc->dev = dev;
|
||||
tc->i2c = client;
|
||||
tc->type = (enum tc3587x5_type)(unsigned long)of_device_get_match_data(dev);
|
||||
|
||||
tc->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node,
|
||||
TC358775_LVDS_OUT0, 0);
|
||||
@ -670,12 +694,9 @@ static int tc_probe(struct i2c_client *client)
|
||||
return ret;
|
||||
}
|
||||
|
||||
tc->stby_gpio = devm_gpiod_get(dev, "stby", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->stby_gpio)) {
|
||||
ret = PTR_ERR(tc->stby_gpio);
|
||||
dev_err(dev, "cannot get stby-gpio %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
tc->stby_gpio = devm_gpiod_get_optional(dev, "stby", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->stby_gpio))
|
||||
return PTR_ERR(tc->stby_gpio);
|
||||
|
||||
tc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->reset_gpio)) {
|
||||
@ -686,6 +707,7 @@ static int tc_probe(struct i2c_client *client)
|
||||
|
||||
tc->bridge.funcs = &tc_bridge_funcs;
|
||||
tc->bridge.of_node = dev->of_node;
|
||||
tc->bridge.pre_enable_prev_first = true;
|
||||
drm_bridge_add(&tc->bridge);
|
||||
|
||||
i2c_set_clientdata(client, tc);
|
||||
@ -709,13 +731,15 @@ static void tc_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc358775_i2c_ids[] = {
|
||||
{ "tc358775", 0 },
|
||||
{ "tc358765", TC358765, },
|
||||
{ "tc358775", TC358775, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids);
|
||||
|
||||
static const struct of_device_id tc358775_of_ids[] = {
|
||||
{ .compatible = "toshiba,tc358775", },
|
||||
{ .compatible = "toshiba,tc358765", .data = (void *)TC358765, },
|
||||
{ .compatible = "toshiba,tc358775", .data = (void *)TC358775, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tc358775_of_ids);
|
||||
|
@ -57,6 +57,16 @@ static void list_insert_sorted(struct drm_buddy *mm,
|
||||
__list_add(&block->link, node->link.prev, &node->link);
|
||||
}
|
||||
|
||||
static void clear_reset(struct drm_buddy_block *block)
|
||||
{
|
||||
block->header &= ~DRM_BUDDY_HEADER_CLEAR;
|
||||
}
|
||||
|
||||
static void mark_cleared(struct drm_buddy_block *block)
|
||||
{
|
||||
block->header |= DRM_BUDDY_HEADER_CLEAR;
|
||||
}
|
||||
|
||||
static void mark_allocated(struct drm_buddy_block *block)
|
||||
{
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
@ -82,6 +92,133 @@ static void mark_split(struct drm_buddy_block *block)
|
||||
list_del(&block->link);
|
||||
}
|
||||
|
||||
static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
|
||||
{
|
||||
return s1 <= e2 && e1 >= s2;
|
||||
}
|
||||
|
||||
static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
|
||||
{
|
||||
return s1 <= s2 && e1 >= e2;
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
__get_buddy(struct drm_buddy_block *block)
|
||||
{
|
||||
struct drm_buddy_block *parent;
|
||||
|
||||
parent = block->parent;
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (parent->left == block)
|
||||
return parent->right;
|
||||
|
||||
return parent->left;
|
||||
}
|
||||
|
||||
static unsigned int __drm_buddy_free(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block,
|
||||
bool force_merge)
|
||||
{
|
||||
struct drm_buddy_block *parent;
|
||||
unsigned int order;
|
||||
|
||||
while ((parent = block->parent)) {
|
||||
struct drm_buddy_block *buddy;
|
||||
|
||||
buddy = __get_buddy(block);
|
||||
|
||||
if (!drm_buddy_block_is_free(buddy))
|
||||
break;
|
||||
|
||||
if (!force_merge) {
|
||||
/*
|
||||
* Check the block and its buddy clear state and exit
|
||||
* the loop if they both have the dissimilar state.
|
||||
*/
|
||||
if (drm_buddy_block_is_clear(block) !=
|
||||
drm_buddy_block_is_clear(buddy))
|
||||
break;
|
||||
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mark_cleared(parent);
|
||||
}
|
||||
|
||||
list_del(&buddy->link);
|
||||
if (force_merge && drm_buddy_block_is_clear(buddy))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, buddy);
|
||||
|
||||
drm_block_free(mm, block);
|
||||
drm_block_free(mm, buddy);
|
||||
|
||||
block = parent;
|
||||
}
|
||||
|
||||
order = drm_buddy_block_order(block);
|
||||
mark_free(mm, block);
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
static int __force_merge(struct drm_buddy *mm,
|
||||
u64 start,
|
||||
u64 end,
|
||||
unsigned int min_order)
|
||||
{
|
||||
unsigned int order;
|
||||
int i;
|
||||
|
||||
if (!min_order)
|
||||
return -ENOMEM;
|
||||
|
||||
if (min_order > mm->max_order)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = min_order - 1; i >= 0; i--) {
|
||||
struct drm_buddy_block *block, *prev;
|
||||
|
||||
list_for_each_entry_safe_reverse(block, prev, &mm->free_list[i], link) {
|
||||
struct drm_buddy_block *buddy;
|
||||
u64 block_start, block_end;
|
||||
|
||||
if (!block->parent)
|
||||
continue;
|
||||
|
||||
block_start = drm_buddy_block_offset(block);
|
||||
block_end = block_start + drm_buddy_block_size(mm, block) - 1;
|
||||
|
||||
if (!contains(start, end, block_start, block_end))
|
||||
continue;
|
||||
|
||||
buddy = __get_buddy(block);
|
||||
if (!drm_buddy_block_is_free(buddy))
|
||||
continue;
|
||||
|
||||
WARN_ON(drm_buddy_block_is_clear(block) ==
|
||||
drm_buddy_block_is_clear(buddy));
|
||||
|
||||
/*
|
||||
* If the prev block is same as buddy, don't access the
|
||||
* block in the next iteration as we would free the
|
||||
* buddy block as part of the free function.
|
||||
*/
|
||||
if (prev == buddy)
|
||||
prev = list_prev_entry(prev, link);
|
||||
|
||||
list_del(&block->link);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
|
||||
order = __drm_buddy_free(mm, block, true);
|
||||
if (order >= min_order)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_buddy_init - init memory manager
|
||||
*
|
||||
@ -186,11 +323,21 @@ EXPORT_SYMBOL(drm_buddy_init);
|
||||
*/
|
||||
void drm_buddy_fini(struct drm_buddy *mm)
|
||||
{
|
||||
u64 root_size, size;
|
||||
unsigned int order;
|
||||
int i;
|
||||
|
||||
size = mm->size;
|
||||
|
||||
for (i = 0; i < mm->n_roots; ++i) {
|
||||
order = ilog2(size) - ilog2(mm->chunk_size);
|
||||
__force_merge(mm, 0, size, order);
|
||||
|
||||
WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
|
||||
drm_block_free(mm, mm->roots[i]);
|
||||
|
||||
root_size = mm->chunk_size << order;
|
||||
size -= root_size;
|
||||
}
|
||||
|
||||
WARN_ON(mm->avail != mm->size);
|
||||
@ -223,26 +370,17 @@ static int split_block(struct drm_buddy *mm,
|
||||
mark_free(mm, block->left);
|
||||
mark_free(mm, block->right);
|
||||
|
||||
if (drm_buddy_block_is_clear(block)) {
|
||||
mark_cleared(block->left);
|
||||
mark_cleared(block->right);
|
||||
clear_reset(block);
|
||||
}
|
||||
|
||||
mark_split(block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
__get_buddy(struct drm_buddy_block *block)
|
||||
{
|
||||
struct drm_buddy_block *parent;
|
||||
|
||||
parent = block->parent;
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (parent->left == block)
|
||||
return parent->right;
|
||||
|
||||
return parent->left;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_get_buddy - get buddy address
|
||||
*
|
||||
@ -260,30 +398,6 @@ drm_get_buddy(struct drm_buddy_block *block)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_buddy);
|
||||
|
||||
static void __drm_buddy_free(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
struct drm_buddy_block *parent;
|
||||
|
||||
while ((parent = block->parent)) {
|
||||
struct drm_buddy_block *buddy;
|
||||
|
||||
buddy = __get_buddy(block);
|
||||
|
||||
if (!drm_buddy_block_is_free(buddy))
|
||||
break;
|
||||
|
||||
list_del(&buddy->link);
|
||||
|
||||
drm_block_free(mm, block);
|
||||
drm_block_free(mm, buddy);
|
||||
|
||||
block = parent;
|
||||
}
|
||||
|
||||
mark_free(mm, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_buddy_free_block - free a block
|
||||
*
|
||||
@ -295,42 +409,74 @@ void drm_buddy_free_block(struct drm_buddy *mm,
|
||||
{
|
||||
BUG_ON(!drm_buddy_block_is_allocated(block));
|
||||
mm->avail += drm_buddy_block_size(mm, block);
|
||||
__drm_buddy_free(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail += drm_buddy_block_size(mm, block);
|
||||
|
||||
__drm_buddy_free(mm, block, false);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_free_block);
|
||||
|
||||
static void __drm_buddy_free_list(struct drm_buddy *mm,
|
||||
struct list_head *objects,
|
||||
bool mark_clear,
|
||||
bool mark_dirty)
|
||||
{
|
||||
struct drm_buddy_block *block, *on;
|
||||
|
||||
WARN_ON(mark_dirty && mark_clear);
|
||||
|
||||
list_for_each_entry_safe(block, on, objects, link) {
|
||||
if (mark_clear)
|
||||
mark_cleared(block);
|
||||
else if (mark_dirty)
|
||||
clear_reset(block);
|
||||
drm_buddy_free_block(mm, block);
|
||||
cond_resched();
|
||||
}
|
||||
INIT_LIST_HEAD(objects);
|
||||
}
|
||||
|
||||
static void drm_buddy_free_list_internal(struct drm_buddy *mm,
|
||||
struct list_head *objects)
|
||||
{
|
||||
/*
|
||||
* Don't touch the clear/dirty bit, since allocation is still internal
|
||||
* at this point. For example we might have just failed part of the
|
||||
* allocation.
|
||||
*/
|
||||
__drm_buddy_free_list(mm, objects, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_buddy_free_list - free blocks
|
||||
*
|
||||
* @mm: DRM buddy manager
|
||||
* @objects: input list head to free blocks
|
||||
* @flags: optional flags like DRM_BUDDY_CLEARED
|
||||
*/
|
||||
void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects)
|
||||
void drm_buddy_free_list(struct drm_buddy *mm,
|
||||
struct list_head *objects,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct drm_buddy_block *block, *on;
|
||||
bool mark_clear = flags & DRM_BUDDY_CLEARED;
|
||||
|
||||
list_for_each_entry_safe(block, on, objects, link) {
|
||||
drm_buddy_free_block(mm, block);
|
||||
cond_resched();
|
||||
}
|
||||
INIT_LIST_HEAD(objects);
|
||||
__drm_buddy_free_list(mm, objects, mark_clear, !mark_clear);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_free_list);
|
||||
|
||||
static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
|
||||
static bool block_incompatible(struct drm_buddy_block *block, unsigned int flags)
|
||||
{
|
||||
return s1 <= e2 && e1 >= s2;
|
||||
}
|
||||
bool needs_clear = flags & DRM_BUDDY_CLEAR_ALLOCATION;
|
||||
|
||||
static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
|
||||
{
|
||||
return s1 <= s2 && e1 >= e2;
|
||||
return needs_clear != drm_buddy_block_is_clear(block);
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
alloc_range_bias(struct drm_buddy *mm,
|
||||
u64 start, u64 end,
|
||||
unsigned int order)
|
||||
__alloc_range_bias(struct drm_buddy *mm,
|
||||
u64 start, u64 end,
|
||||
unsigned int order,
|
||||
unsigned long flags,
|
||||
bool fallback)
|
||||
{
|
||||
u64 req_size = mm->chunk_size << order;
|
||||
struct drm_buddy_block *block;
|
||||
@ -379,6 +525,9 @@ alloc_range_bias(struct drm_buddy *mm,
|
||||
|
||||
if (contains(start, end, block_start, block_end) &&
|
||||
order == drm_buddy_block_order(block)) {
|
||||
if (!fallback && block_incompatible(block, flags))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Find the free block within the range.
|
||||
*/
|
||||
@ -410,30 +559,57 @@ err_undo:
|
||||
if (buddy &&
|
||||
(drm_buddy_block_is_free(block) &&
|
||||
drm_buddy_block_is_free(buddy)))
|
||||
__drm_buddy_free(mm, block);
|
||||
__drm_buddy_free(mm, block, false);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
get_maxblock(struct drm_buddy *mm, unsigned int order)
|
||||
__drm_buddy_alloc_range_bias(struct drm_buddy *mm,
|
||||
u64 start, u64 end,
|
||||
unsigned int order,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct drm_buddy_block *max_block = NULL, *node;
|
||||
struct drm_buddy_block *block;
|
||||
bool fallback = false;
|
||||
|
||||
block = __alloc_range_bias(mm, start, end, order,
|
||||
flags, fallback);
|
||||
if (IS_ERR(block) && mm->clear_avail)
|
||||
return __alloc_range_bias(mm, start, end, order,
|
||||
flags, !fallback);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
get_maxblock(struct drm_buddy *mm, unsigned int order,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct drm_buddy_block *max_block = NULL, *block = NULL;
|
||||
unsigned int i;
|
||||
|
||||
for (i = order; i <= mm->max_order; ++i) {
|
||||
if (!list_empty(&mm->free_list[i])) {
|
||||
node = list_last_entry(&mm->free_list[i],
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (!max_block) {
|
||||
max_block = node;
|
||||
continue;
|
||||
}
|
||||
struct drm_buddy_block *tmp_block;
|
||||
|
||||
if (drm_buddy_block_offset(node) >
|
||||
drm_buddy_block_offset(max_block)) {
|
||||
max_block = node;
|
||||
}
|
||||
list_for_each_entry_reverse(tmp_block, &mm->free_list[i], link) {
|
||||
if (block_incompatible(tmp_block, flags))
|
||||
continue;
|
||||
|
||||
block = tmp_block;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
if (!max_block) {
|
||||
max_block = block;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (drm_buddy_block_offset(block) >
|
||||
drm_buddy_block_offset(max_block)) {
|
||||
max_block = block;
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,11 +626,29 @@ alloc_from_freelist(struct drm_buddy *mm,
|
||||
int err;
|
||||
|
||||
if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
|
||||
block = get_maxblock(mm, order);
|
||||
block = get_maxblock(mm, order, flags);
|
||||
if (block)
|
||||
/* Store the obtained block order */
|
||||
tmp = drm_buddy_block_order(block);
|
||||
} else {
|
||||
for (tmp = order; tmp <= mm->max_order; ++tmp) {
|
||||
struct drm_buddy_block *tmp_block;
|
||||
|
||||
list_for_each_entry_reverse(tmp_block, &mm->free_list[tmp], link) {
|
||||
if (block_incompatible(tmp_block, flags))
|
||||
continue;
|
||||
|
||||
block = tmp_block;
|
||||
break;
|
||||
}
|
||||
|
||||
if (block)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
/* Fallback method */
|
||||
for (tmp = order; tmp <= mm->max_order; ++tmp) {
|
||||
if (!list_empty(&mm->free_list[tmp])) {
|
||||
block = list_last_entry(&mm->free_list[tmp],
|
||||
@ -464,10 +658,10 @@ alloc_from_freelist(struct drm_buddy *mm,
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!block)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
if (!block)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
}
|
||||
|
||||
BUG_ON(!drm_buddy_block_is_free(block));
|
||||
|
||||
@ -483,7 +677,7 @@ alloc_from_freelist(struct drm_buddy *mm,
|
||||
|
||||
err_undo:
|
||||
if (tmp != order)
|
||||
__drm_buddy_free(mm, block);
|
||||
__drm_buddy_free(mm, block, false);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@ -526,16 +720,18 @@ static int __alloc_range(struct drm_buddy *mm,
|
||||
}
|
||||
|
||||
if (contains(start, end, block_start, block_end)) {
|
||||
if (!drm_buddy_block_is_free(block)) {
|
||||
if (drm_buddy_block_is_free(block)) {
|
||||
mark_allocated(block);
|
||||
total_allocated += drm_buddy_block_size(mm, block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
list_add_tail(&block->link, &allocated);
|
||||
continue;
|
||||
} else if (!mm->clear_avail) {
|
||||
err = -ENOSPC;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
mark_allocated(block);
|
||||
total_allocated += drm_buddy_block_size(mm, block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
list_add_tail(&block->link, &allocated);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!drm_buddy_block_is_split(block)) {
|
||||
@ -567,14 +763,14 @@ err_undo:
|
||||
if (buddy &&
|
||||
(drm_buddy_block_is_free(block) &&
|
||||
drm_buddy_block_is_free(buddy)))
|
||||
__drm_buddy_free(mm, block);
|
||||
__drm_buddy_free(mm, block, false);
|
||||
|
||||
err_free:
|
||||
if (err == -ENOSPC && total_allocated_on_err) {
|
||||
list_splice_tail(&allocated, blocks);
|
||||
*total_allocated_on_err = total_allocated;
|
||||
} else {
|
||||
drm_buddy_free_list(mm, &allocated);
|
||||
drm_buddy_free_list_internal(mm, &allocated);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -640,11 +836,11 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm,
|
||||
list_splice(&blocks_lhs, blocks);
|
||||
return 0;
|
||||
} else if (err != -ENOSPC) {
|
||||
drm_buddy_free_list(mm, blocks);
|
||||
drm_buddy_free_list_internal(mm, blocks);
|
||||
return err;
|
||||
}
|
||||
/* Free blocks for the next iteration */
|
||||
drm_buddy_free_list(mm, blocks);
|
||||
drm_buddy_free_list_internal(mm, blocks);
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
@ -700,6 +896,8 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
|
||||
list_del(&block->link);
|
||||
mark_free(mm, block);
|
||||
mm->avail += drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail += drm_buddy_block_size(mm, block);
|
||||
|
||||
/* Prevent recursively freeing this node */
|
||||
parent = block->parent;
|
||||
@ -711,6 +909,8 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
|
||||
if (err) {
|
||||
mark_allocated(block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
list_add(&block->link, blocks);
|
||||
}
|
||||
|
||||
@ -719,13 +919,28 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_block_trim);
|
||||
|
||||
static struct drm_buddy_block *
|
||||
__drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
u64 start, u64 end,
|
||||
unsigned int order,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (flags & DRM_BUDDY_RANGE_ALLOCATION)
|
||||
/* Allocate traversing within the range */
|
||||
return __drm_buddy_alloc_range_bias(mm, start, end,
|
||||
order, flags);
|
||||
else
|
||||
/* Allocate from freelist */
|
||||
return alloc_from_freelist(mm, order, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_buddy_alloc_blocks - allocate power-of-two blocks
|
||||
*
|
||||
* @mm: DRM buddy manager to allocate from
|
||||
* @start: start of the allowed range for this block
|
||||
* @end: end of the allowed range for this block
|
||||
* @size: size of the allocation
|
||||
* @size: size of the allocation in bytes
|
||||
* @min_block_size: alignment of the allocation
|
||||
* @blocks: output list head to add allocated blocks
|
||||
* @flags: DRM_BUDDY_*_ALLOCATION flags
|
||||
@ -800,23 +1015,33 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
BUG_ON(order < min_order);
|
||||
|
||||
do {
|
||||
if (flags & DRM_BUDDY_RANGE_ALLOCATION)
|
||||
/* Allocate traversing within the range */
|
||||
block = alloc_range_bias(mm, start, end, order);
|
||||
else
|
||||
/* Allocate from freelist */
|
||||
block = alloc_from_freelist(mm, order, flags);
|
||||
|
||||
block = __drm_buddy_alloc_blocks(mm, start,
|
||||
end,
|
||||
order,
|
||||
flags);
|
||||
if (!IS_ERR(block))
|
||||
break;
|
||||
|
||||
if (order-- == min_order) {
|
||||
/* Try allocation through force merge method */
|
||||
if (mm->clear_avail &&
|
||||
!__force_merge(mm, start, end, min_order)) {
|
||||
block = __drm_buddy_alloc_blocks(mm, start,
|
||||
end,
|
||||
min_order,
|
||||
flags);
|
||||
if (!IS_ERR(block)) {
|
||||
order = min_order;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try contiguous block allocation through
|
||||
* try harder method.
|
||||
*/
|
||||
if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION &&
|
||||
!(flags & DRM_BUDDY_RANGE_ALLOCATION))
|
||||
/*
|
||||
* Try contiguous block allocation through
|
||||
* try harder method
|
||||
*/
|
||||
return __alloc_contig_try_harder(mm,
|
||||
original_size,
|
||||
original_min_size,
|
||||
@ -828,6 +1053,8 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
|
||||
mark_allocated(block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
kmemleak_update_trace(block);
|
||||
list_add_tail(&block->link, &allocated);
|
||||
|
||||
@ -866,7 +1093,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
drm_buddy_free_list(mm, &allocated);
|
||||
drm_buddy_free_list_internal(mm, &allocated);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_alloc_blocks);
|
||||
@ -899,8 +1126,8 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
|
||||
{
|
||||
int order;
|
||||
|
||||
drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB\n",
|
||||
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);
|
||||
drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n",
|
||||
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
|
||||
|
||||
for (order = mm->max_order; order >= 0; order--) {
|
||||
struct drm_buddy_block *block;
|
||||
|
@ -31,7 +31,6 @@ struct drm_edid;
|
||||
#define VESA_IEEE_OUI 0x3a0292
|
||||
|
||||
/* DisplayID Structure versions */
|
||||
#define DISPLAY_ID_STRUCTURE_VER_12 0x12
|
||||
#define DISPLAY_ID_STRUCTURE_VER_20 0x20
|
||||
|
||||
/* DisplayID Structure v1r2 Data Blocks */
|
||||
|
@ -7462,7 +7462,7 @@ static void drm_parse_tiled_block(struct drm_connector *connector,
|
||||
static bool displayid_is_tiled_block(const struct displayid_iter *iter,
|
||||
const struct displayid_block *block)
|
||||
{
|
||||
return (displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_12 &&
|
||||
return (displayid_version(iter) < DISPLAY_ID_STRUCTURE_VER_20 &&
|
||||
block->tag == DATA_BLOCK_TILED_DISPLAY) ||
|
||||
(displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
|
||||
block->tag == DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY);
|
||||
|
@ -1750,7 +1750,7 @@ int drm_plane_create_scaling_filter_property(struct drm_plane *plane,
|
||||
EXPORT_SYMBOL(drm_plane_create_scaling_filter_property);
|
||||
|
||||
/**
|
||||
* drm_plane_add_size_hint_property - create a size hint property
|
||||
* drm_plane_add_size_hints_property - create a size hints property
|
||||
*
|
||||
* @plane: drm plane
|
||||
* @hints: size hints
|
||||
|
@ -23,13 +23,13 @@
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/stdarg.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dynamic_debug.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dynamic_debug.h>
|
||||
#include <linux/stdarg.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_drv.h>
|
||||
|
@ -221,7 +221,7 @@ static int gud_connector_get_modes(struct drm_connector *connector)
|
||||
struct gud_display_mode_req *reqmodes = NULL;
|
||||
struct gud_connector_get_edid_ctx edid_ctx;
|
||||
unsigned int i, num_modes = 0;
|
||||
struct edid *edid = NULL;
|
||||
const struct drm_edid *drm_edid = NULL;
|
||||
int idx, ret;
|
||||
|
||||
if (!drm_dev_enter(connector->dev, &idx))
|
||||
@ -238,13 +238,13 @@ static int gud_connector_get_modes(struct drm_connector *connector)
|
||||
gud_conn_err(connector, "Invalid EDID size", ret);
|
||||
} else if (ret > 0) {
|
||||
edid_ctx.len = ret;
|
||||
edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
|
||||
drm_edid = drm_edid_read_custom(connector, gud_connector_get_edid_block, &edid_ctx);
|
||||
}
|
||||
|
||||
kfree(edid_ctx.buf);
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
|
||||
if (edid && edid_ctx.edid_override)
|
||||
if (drm_edid && edid_ctx.edid_override)
|
||||
goto out;
|
||||
|
||||
reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_NUM_MODES, sizeof(*reqmodes), GFP_KERNEL);
|
||||
@ -276,10 +276,10 @@ static int gud_connector_get_modes(struct drm_connector *connector)
|
||||
}
|
||||
out:
|
||||
if (!num_modes)
|
||||
num_modes = drm_add_edid_modes(connector, edid);
|
||||
num_modes = drm_edid_connector_add_modes(connector);
|
||||
|
||||
kfree(reqmodes);
|
||||
kfree(edid);
|
||||
drm_edid_free(drm_edid);
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return num_modes;
|
||||
|
@ -22,6 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
|
@ -126,7 +126,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
|
||||
return 0;
|
||||
|
||||
err_free_blocks:
|
||||
drm_buddy_free_list(mm, &bman_res->blocks);
|
||||
drm_buddy_free_list(mm, &bman_res->blocks, 0);
|
||||
mutex_unlock(&bman->lock);
|
||||
err_free_res:
|
||||
ttm_resource_fini(man, &bman_res->base);
|
||||
@ -141,7 +141,7 @@ static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
|
||||
struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
|
||||
|
||||
mutex_lock(&bman->lock);
|
||||
drm_buddy_free_list(&bman->mm, &bman_res->blocks);
|
||||
drm_buddy_free_list(&bman->mm, &bman_res->blocks, 0);
|
||||
bman->visible_avail += bman_res->used_visible_size;
|
||||
mutex_unlock(&bman->lock);
|
||||
|
||||
@ -345,7 +345,7 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
|
||||
ttm_set_driver_manager(bdev, type, NULL);
|
||||
|
||||
mutex_lock(&bman->lock);
|
||||
drm_buddy_free_list(mm, &bman->reserved);
|
||||
drm_buddy_free_list(mm, &bman->reserved, 0);
|
||||
drm_buddy_fini(mm);
|
||||
bman->visible_avail += bman->visible_reserved;
|
||||
WARN_ON_ONCE(bman->visible_avail != bman->visible_size);
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
@ -95,6 +95,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mipi_dsi->px_clk);
|
||||
ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000);
|
||||
|
||||
if (ret) {
|
||||
@ -103,6 +104,12 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(mipi_dsi->px_clk);
|
||||
if (ret) {
|
||||
dev_err(mipi_dsi->dev, "Failed to enable DSI Pixel clock (ret %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (mipi_dsi->dsi_device->format) {
|
||||
case MIPI_DSI_FMT_RGB888:
|
||||
dpi_data_format = DPI_COLOR_24BIT;
|
||||
|
@ -2,6 +2,8 @@
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "dpu_hwio.h"
|
||||
#include "dpu_hw_catalog.h"
|
||||
#include "dpu_hw_lm.h"
|
||||
|
@ -1,5 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
@ -553,6 +553,18 @@ config DRM_PANEL_RAYDIUM_RM692E5
|
||||
Say Y here if you want to enable support for Raydium RM692E5-based
|
||||
display panels, such as the one found in the Fairphone 5 smartphone.
|
||||
|
||||
config DRM_PANEL_RAYDIUM_RM69380
|
||||
tristate "Raydium RM69380-based DSI panel"
|
||||
depends on OF && GPIOLIB
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for Raydium RM69380-based
|
||||
display panels.
|
||||
|
||||
This panel controller can be found in the Lenovo Xiaoxin Pad Pro 2021
|
||||
in combination with an EDO OLED panel.
|
||||
|
||||
config DRM_PANEL_RONBO_RB070D30
|
||||
tristate "Ronbo Electronics RB070D30 panel"
|
||||
depends on OF
|
||||
|
@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen
|
||||
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o
|
||||
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
|
||||
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o
|
||||
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o
|
||||
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o
|
||||
|
@ -242,7 +242,7 @@ struct panel_edp {
|
||||
|
||||
const struct edp_panel_entry *detected_panel;
|
||||
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
struct drm_display_mode override_mode;
|
||||
|
||||
@ -617,13 +617,16 @@ static int panel_edp_get_modes(struct drm_panel *panel,
|
||||
if (p->ddc) {
|
||||
pm_runtime_get_sync(panel->dev);
|
||||
|
||||
if (!p->edid)
|
||||
p->edid = drm_get_edid(connector, p->ddc);
|
||||
if (!p->drm_edid)
|
||||
p->drm_edid = drm_edid_read_ddc(connector, p->ddc);
|
||||
|
||||
drm_edid_connector_update(connector, p->drm_edid);
|
||||
|
||||
/*
|
||||
* If both edid and hard-coded modes exists, skip edid modes to
|
||||
* avoid multiple preferred modes.
|
||||
*/
|
||||
if (p->edid && !has_hard_coded_modes) {
|
||||
if (p->drm_edid && !has_hard_coded_modes) {
|
||||
if (has_override_edid_mode) {
|
||||
/*
|
||||
* override_edid_mode is specified. Use
|
||||
@ -632,7 +635,7 @@ static int panel_edp_get_modes(struct drm_panel *panel,
|
||||
num += panel_edp_override_edid_mode(p, connector,
|
||||
p->detected_panel->override_edid_mode);
|
||||
} else {
|
||||
num += drm_add_edid_modes(connector, p->edid);
|
||||
num += drm_edid_connector_add_modes(connector);
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,8 +984,8 @@ static void panel_edp_remove(struct device *dev)
|
||||
if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
|
||||
put_device(&panel->ddc->dev);
|
||||
|
||||
kfree(panel->edid);
|
||||
panel->edid = NULL;
|
||||
drm_edid_free(panel->drm_edid);
|
||||
panel->drm_edid = NULL;
|
||||
}
|
||||
|
||||
static void panel_edp_shutdown(struct device *dev)
|
||||
@ -2075,6 +2078,8 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
|
||||
EDP_PANEL_ENTRY('C', 'S', 'O', 0x1200, &delay_200_500_e50_p2e200, "MNC207QS1-1"),
|
||||
|
||||
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1100, &delay_200_500_e80_d50, "MNB601LS1-1"),
|
||||
|
||||
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d51, &delay_200_500_e200, "Unknown"),
|
||||
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5b, &delay_200_500_e200, "Unknown"),
|
||||
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5c, &delay_200_500_e200, "MB116AN01-2"),
|
||||
|
@ -247,6 +247,7 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi)
|
||||
|
||||
drm_panel_init(&ctx->panel, dev, &jdi_fhd_r63452_panel_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
ctx->panel.prepare_prev_first = true;
|
||||
|
||||
ret = drm_panel_of_backlight(&ctx->panel);
|
||||
if (ret)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -605,21 +605,16 @@ static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
|
||||
struct device *dev = &pinfo->link->dev;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pinfo->supplies); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(pinfo->supplies); i++) {
|
||||
pinfo->supplies[i].supply = nt36672a_regulator_names[i];
|
||||
pinfo->supplies[i].init_load_uA = nt36672a_regulator_enable_loads[i];
|
||||
}
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pinfo->supplies),
|
||||
pinfo->supplies);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to get regulators\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pinfo->supplies); i++) {
|
||||
ret = regulator_set_load(pinfo->supplies[i].consumer,
|
||||
nt36672a_regulator_enable_loads[i]);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set regulator enable loads\n");
|
||||
}
|
||||
|
||||
pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pinfo->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio),
|
||||
|
@ -25,12 +25,6 @@ static const unsigned long regulator_enable_loads[] = {
|
||||
100000,
|
||||
};
|
||||
|
||||
static const unsigned long regulator_disable_loads[] = {
|
||||
80,
|
||||
100,
|
||||
100,
|
||||
};
|
||||
|
||||
struct panel_desc {
|
||||
const struct drm_display_mode *display_mode;
|
||||
u32 width_mm;
|
||||
@ -349,17 +343,7 @@ static int nt36672e_1080x2408_60hz_init(struct mipi_dsi_device *dsi)
|
||||
static int nt36672e_power_on(struct nt36672e_panel *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
|
||||
ret = regulator_set_load(ctx->supplies[i].consumer,
|
||||
regulator_enable_loads[i]);
|
||||
if (ret) {
|
||||
dev_err(&dsi->dev, "regulator set load failed for supply %s: %d\n",
|
||||
ctx->supplies[i].supply, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret < 0) {
|
||||
@ -385,20 +369,9 @@ static int nt36672e_power_off(struct nt36672e_panel *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
|
||||
ret = regulator_set_load(ctx->supplies[i].consumer,
|
||||
regulator_disable_loads[i]);
|
||||
if (ret) {
|
||||
dev_err(&dsi->dev, "regulator set load failed for supply %s: %d\n",
|
||||
ctx->supplies[i].supply, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret)
|
||||
dev_err(&dsi->dev, "regulator bulk disable failed: %d\n", ret);
|
||||
@ -567,8 +540,10 @@ static int nt36672e_panel_probe(struct mipi_dsi_device *dsi)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
|
||||
ctx->supplies[i].supply = regulator_names[i];
|
||||
ctx->supplies[i].init_load_uA = regulator_enable_loads[i];
|
||||
}
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
|
344
drivers/gpu/drm/panel/panel-raydium-rm69380.c
Normal file
344
drivers/gpu/drm/panel/panel-raydium-rm69380.c
Normal file
@ -0,0 +1,344 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
|
||||
* Copyright (c) 2024 David Wronek <david@mainlining.org>
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
struct rm69380_panel {
|
||||
struct drm_panel panel;
|
||||
struct mipi_dsi_device *dsi[2];
|
||||
struct regulator_bulk_data supplies[2];
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static inline
|
||||
struct rm69380_panel *to_rm69380_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct rm69380_panel, panel);
|
||||
}
|
||||
|
||||
static void rm69380_reset(struct rm69380_panel *ctx)
|
||||
{
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
usleep_range(15000, 16000);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
usleep_range(10000, 11000);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
msleep(30);
|
||||
}
|
||||
|
||||
static int rm69380_on(struct rm69380_panel *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi[0];
|
||||
struct device *dev = &dsi->dev;
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
if (ctx->dsi[1])
|
||||
ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
|
||||
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0xd4);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0x00, 0x80);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0xd0);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0x48, 0x00);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x26);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0x75, 0x3f);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0x1d, 0x1a);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x00);
|
||||
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x28);
|
||||
mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x08);
|
||||
|
||||
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set tear on: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
msleep(20);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set display on: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
msleep(36);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm69380_off(struct rm69380_panel *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi[0];
|
||||
struct device *dev = &dsi->dev;
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
if (ctx->dsi[1])
|
||||
ctx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set display off: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
msleep(35);
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm69380_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct rm69380_panel *ctx = to_rm69380_panel(panel);
|
||||
struct device *dev = &ctx->dsi[0]->dev;
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enable regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rm69380_reset(ctx);
|
||||
|
||||
ret = rm69380_on(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to initialize panel: %d\n", ret);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm69380_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct rm69380_panel *ctx = to_rm69380_panel(panel);
|
||||
struct device *dev = &ctx->dsi[0]->dev;
|
||||
int ret;
|
||||
|
||||
ret = rm69380_off(ctx);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
|
||||
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode rm69380_mode = {
|
||||
.clock = (2560 + 32 + 12 + 38) * (1600 + 20 + 4 + 8) * 90 / 1000,
|
||||
.hdisplay = 2560,
|
||||
.hsync_start = 2560 + 32,
|
||||
.hsync_end = 2560 + 32 + 12,
|
||||
.htotal = 2560 + 32 + 12 + 38,
|
||||
.vdisplay = 1600,
|
||||
.vsync_start = 1600 + 20,
|
||||
.vsync_end = 1600 + 20 + 4,
|
||||
.vtotal = 1600 + 20 + 4 + 8,
|
||||
.width_mm = 248,
|
||||
.height_mm = 155,
|
||||
.type = DRM_MODE_TYPE_DRIVER,
|
||||
};
|
||||
|
||||
static int rm69380_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
return drm_connector_helper_get_modes_fixed(connector, &rm69380_mode);
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs rm69380_panel_funcs = {
|
||||
.prepare = rm69380_prepare,
|
||||
.unprepare = rm69380_unprepare,
|
||||
.get_modes = rm69380_get_modes,
|
||||
};
|
||||
|
||||
static int rm69380_bl_update_status(struct backlight_device *bl)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||
u16 brightness = backlight_get_brightness(bl);
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm69380_bl_get_brightness(struct backlight_device *bl)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||
u16 brightness;
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
static const struct backlight_ops rm69380_bl_ops = {
|
||||
.update_status = rm69380_bl_update_status,
|
||||
.get_brightness = rm69380_bl_get_brightness,
|
||||
};
|
||||
|
||||
static struct backlight_device *
|
||||
rm69380_create_backlight(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
const struct backlight_properties props = {
|
||||
.type = BACKLIGHT_RAW,
|
||||
.brightness = 511,
|
||||
.max_brightness = 2047,
|
||||
};
|
||||
|
||||
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
|
||||
&rm69380_bl_ops, &props);
|
||||
}
|
||||
|
||||
static int rm69380_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct mipi_dsi_host *dsi_sec_host;
|
||||
struct rm69380_panel *ctx;
|
||||
struct device *dev = &dsi->dev;
|
||||
struct device_node *dsi_sec;
|
||||
int ret, i;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->supplies[0].supply = "vddio";
|
||||
ctx->supplies[1].supply = "avdd";
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to get regulators\n");
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ctx->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
||||
"Failed to get reset-gpios\n");
|
||||
|
||||
dsi_sec = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
|
||||
|
||||
if (dsi_sec) {
|
||||
const struct mipi_dsi_device_info info = { "RM69380 DSI1", 0,
|
||||
dsi_sec };
|
||||
|
||||
dsi_sec_host = of_find_mipi_dsi_host_by_node(dsi_sec);
|
||||
of_node_put(dsi_sec);
|
||||
if (!dsi_sec_host)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER,
|
||||
"Cannot get secondary DSI host\n");
|
||||
|
||||
ctx->dsi[1] =
|
||||
devm_mipi_dsi_device_register_full(dev, dsi_sec_host, &info);
|
||||
if (IS_ERR(ctx->dsi[1]))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]),
|
||||
"Cannot get secondary DSI node\n");
|
||||
|
||||
mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
|
||||
}
|
||||
|
||||
ctx->dsi[0] = dsi;
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
drm_panel_init(&ctx->panel, dev, &rm69380_panel_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
ctx->panel.prepare_prev_first = true;
|
||||
|
||||
ctx->panel.backlight = rm69380_create_backlight(dsi);
|
||||
if (IS_ERR(ctx->panel.backlight))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
|
||||
"Failed to create backlight\n");
|
||||
|
||||
drm_panel_add(&ctx->panel);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
|
||||
if (!ctx->dsi[i])
|
||||
continue;
|
||||
|
||||
dev_dbg(&ctx->dsi[i]->dev, "Binding DSI %d\n", i);
|
||||
|
||||
ctx->dsi[i]->lanes = 4;
|
||||
ctx->dsi[i]->format = MIPI_DSI_FMT_RGB888;
|
||||
ctx->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_CLOCK_NON_CONTINUOUS;
|
||||
|
||||
ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
|
||||
if (ret < 0) {
|
||||
drm_panel_remove(&ctx->panel);
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to attach to DSI%d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rm69380_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct rm69380_panel *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
drm_panel_remove(&ctx->panel);
|
||||
}
|
||||
|
||||
static const struct of_device_id rm69380_of_match[] = {
|
||||
{ .compatible = "lenovo,j716f-edo-rm69380" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rm69380_of_match);
|
||||
|
||||
static struct mipi_dsi_driver rm69380_panel_driver = {
|
||||
.probe = rm69380_probe,
|
||||
.remove = rm69380_remove,
|
||||
.driver = {
|
||||
.name = "panel-raydium-rm69380",
|
||||
.of_match_table = rm69380_of_match,
|
||||
},
|
||||
};
|
||||
module_mipi_dsi_driver(rm69380_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("David Wronek <david@mainlining.org");
|
||||
MODULE_DESCRIPTION("DRM driver for Raydium RM69380-equipped DSI panels");
|
||||
MODULE_LICENSE("GPL");
|
@ -36,7 +36,7 @@ struct atana33xc20_panel {
|
||||
struct gpio_desc *el_on3_gpio;
|
||||
struct drm_dp_aux *aux;
|
||||
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
ktime_t powered_off_time;
|
||||
ktime_t powered_on_time;
|
||||
@ -253,9 +253,12 @@ static int atana33xc20_get_modes(struct drm_panel *panel,
|
||||
|
||||
pm_runtime_get_sync(panel->dev);
|
||||
|
||||
if (!p->edid)
|
||||
p->edid = drm_get_edid(connector, &aux_ep->aux->ddc);
|
||||
num = drm_add_edid_modes(connector, p->edid);
|
||||
if (!p->drm_edid)
|
||||
p->drm_edid = drm_edid_read_ddc(connector, &aux_ep->aux->ddc);
|
||||
|
||||
drm_edid_connector_update(connector, p->drm_edid);
|
||||
|
||||
num = drm_edid_connector_add_modes(connector);
|
||||
|
||||
pm_runtime_mark_last_busy(panel->dev);
|
||||
pm_runtime_put_autosuspend(panel->dev);
|
||||
@ -351,7 +354,7 @@ static void atana33xc20_remove(struct dp_aux_ep_device *aux_ep)
|
||||
drm_panel_disable(&panel->base);
|
||||
drm_panel_unprepare(&panel->base);
|
||||
|
||||
kfree(panel->edid);
|
||||
drm_edid_free(panel->drm_edid);
|
||||
}
|
||||
|
||||
static void atana33xc20_shutdown(struct dp_aux_ep_device *aux_ep)
|
||||
|
@ -151,7 +151,7 @@ struct panel_simple {
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
struct drm_display_mode override_mode;
|
||||
|
||||
@ -309,8 +309,8 @@ static int panel_simple_suspend(struct device *dev)
|
||||
regulator_disable(p->supply);
|
||||
p->unprepared_time = ktime_get_boottime();
|
||||
|
||||
kfree(p->edid);
|
||||
p->edid = NULL;
|
||||
drm_edid_free(p->drm_edid);
|
||||
p->drm_edid = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -399,11 +399,12 @@ static int panel_simple_get_modes(struct drm_panel *panel,
|
||||
if (p->ddc) {
|
||||
pm_runtime_get_sync(panel->dev);
|
||||
|
||||
if (!p->edid)
|
||||
p->edid = drm_get_edid(connector, p->ddc);
|
||||
if (!p->drm_edid)
|
||||
p->drm_edid = drm_edid_read_ddc(connector, p->ddc);
|
||||
|
||||
if (p->edid)
|
||||
num += drm_add_edid_modes(connector, p->edid);
|
||||
drm_edid_connector_update(connector, p->drm_edid);
|
||||
|
||||
num += drm_edid_connector_add_modes(connector);
|
||||
|
||||
pm_runtime_mark_last_busy(panel->dev);
|
||||
pm_runtime_put_autosuspend(panel->dev);
|
||||
|
@ -197,7 +197,9 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
|
||||
ctx->dsi = dsi;
|
||||
|
||||
ctx->supplies[0].supply = "vdda";
|
||||
ctx->supplies[0].init_load_uA = 32000;
|
||||
ctx->supplies[1].supply = "vdd3p3";
|
||||
ctx->supplies[1].init_load_uA = 13200;
|
||||
|
||||
ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
@ -227,22 +229,8 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
|
||||
goto err_dsi_attach;
|
||||
}
|
||||
|
||||
ret = regulator_set_load(ctx->supplies[0].consumer, 32000);
|
||||
if (ret) {
|
||||
dev_err(dev, "regulator set load failed for vdda supply ret = %d\n", ret);
|
||||
goto err_set_load;
|
||||
}
|
||||
|
||||
ret = regulator_set_load(ctx->supplies[1].consumer, 13200);
|
||||
if (ret) {
|
||||
dev_err(dev, "regulator set load failed for vdd3p3 supply ret = %d\n", ret);
|
||||
goto err_set_load;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_set_load:
|
||||
mipi_dsi_detach(dsi);
|
||||
err_dsi_attach:
|
||||
drm_panel_remove(&ctx->panel);
|
||||
return ret;
|
||||
|
@ -2546,7 +2546,7 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
|
||||
{
|
||||
struct panthor_scheduler *sched = ptdev->scheduler;
|
||||
struct panthor_csg_slots_upd_ctx upd_ctx;
|
||||
u64 suspended_slots, faulty_slots;
|
||||
u32 suspended_slots, faulty_slots;
|
||||
struct panthor_group *group;
|
||||
u32 i;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -26,11 +26,12 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/iosys-map.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
* Christian König
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
#include "radeon.h"
|
||||
|
@ -21,6 +21,7 @@
|
||||
* Alex Deucher <alexdeucher@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -27,6 +27,8 @@
|
||||
* Christian König
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
* Dave Airlie
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
* Jerome Glisse
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -262,20 +262,21 @@ static const struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
|
||||
static int cdn_dp_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct cdn_dp_device *dp = connector_to_dp(connector);
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dp->lock);
|
||||
edid = dp->edid;
|
||||
if (edid) {
|
||||
|
||||
if (dp->drm_edid) {
|
||||
/* FIXME: get rid of drm_edid_raw() */
|
||||
const struct edid *edid = drm_edid_raw(dp->drm_edid);
|
||||
|
||||
DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n",
|
||||
edid->width_cm, edid->height_cm);
|
||||
|
||||
dp->sink_has_audio = drm_detect_monitor_audio(edid);
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
}
|
||||
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
|
||||
mutex_unlock(&dp->lock);
|
||||
|
||||
return ret;
|
||||
@ -380,9 +381,13 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(dp->edid);
|
||||
dp->edid = drm_do_get_edid(&dp->connector,
|
||||
cdn_dp_get_edid_block, dp);
|
||||
drm_edid_free(dp->drm_edid);
|
||||
dp->drm_edid = drm_edid_read_custom(&dp->connector,
|
||||
cdn_dp_get_edid_block, dp);
|
||||
drm_edid_connector_update(&dp->connector, dp->drm_edid);
|
||||
|
||||
dp->sink_has_audio = dp->connector.display_info.has_audio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -488,8 +493,8 @@ static int cdn_dp_disable(struct cdn_dp_device *dp)
|
||||
dp->max_lanes = 0;
|
||||
dp->max_rate = 0;
|
||||
if (!dp->connected) {
|
||||
kfree(dp->edid);
|
||||
dp->edid = NULL;
|
||||
drm_edid_free(dp->drm_edid);
|
||||
dp->drm_edid = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1131,8 +1136,8 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
|
||||
pm_runtime_disable(dev);
|
||||
if (dp->fw_loaded)
|
||||
release_firmware(dp->fw);
|
||||
kfree(dp->edid);
|
||||
dp->edid = NULL;
|
||||
drm_edid_free(dp->drm_edid);
|
||||
dp->drm_edid = NULL;
|
||||
}
|
||||
|
||||
static const struct component_ops cdn_dp_component_ops = {
|
||||
@ -1259,7 +1264,6 @@ struct platform_driver cdn_dp_driver = {
|
||||
.shutdown = cdn_dp_shutdown,
|
||||
.driver = {
|
||||
.name = "cdn-dp",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cdn_dp_dt_ids,
|
||||
.pm = &cdn_dp_pm_ops,
|
||||
},
|
||||
|
@ -70,7 +70,7 @@ struct cdn_dp_device {
|
||||
struct drm_display_mode mode;
|
||||
struct platform_device *audio_pdev;
|
||||
struct work_struct event_work;
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
struct mutex lock;
|
||||
bool connected;
|
||||
|
@ -606,18 +606,16 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret = 0;
|
||||
|
||||
if (!hdmi->ddc)
|
||||
return 0;
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->ddc);
|
||||
if (edid) {
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
drm_edid = drm_edid_read_ddc(connector, hdmi->ddc);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -466,18 +466,16 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret = 0;
|
||||
|
||||
if (!hdmi->ddc)
|
||||
return 0;
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->ddc);
|
||||
if (edid) {
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
drm_edid = drm_edid_read_ddc(connector, hdmi->ddc);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -706,6 +706,8 @@ static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
|
||||
const struct drm_format_info *info;
|
||||
u16 hor_scl_mode, ver_scl_mode;
|
||||
u16 hscl_filter_mode, vscl_filter_mode;
|
||||
uint16_t cbcr_src_w = src_w;
|
||||
uint16_t cbcr_src_h = src_h;
|
||||
u8 gt2 = 0;
|
||||
u8 gt4 = 0;
|
||||
u32 val;
|
||||
@ -763,27 +765,27 @@ static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
|
||||
vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode);
|
||||
|
||||
if (info->is_yuv) {
|
||||
src_w /= info->hsub;
|
||||
src_h /= info->vsub;
|
||||
cbcr_src_w /= info->hsub;
|
||||
cbcr_src_h /= info->vsub;
|
||||
|
||||
gt4 = 0;
|
||||
gt2 = 0;
|
||||
|
||||
if (src_h >= (4 * dst_h)) {
|
||||
if (cbcr_src_h >= (4 * dst_h)) {
|
||||
gt4 = 1;
|
||||
src_h >>= 2;
|
||||
} else if (src_h >= (2 * dst_h)) {
|
||||
cbcr_src_h >>= 2;
|
||||
} else if (cbcr_src_h >= (2 * dst_h)) {
|
||||
gt2 = 1;
|
||||
src_h >>= 1;
|
||||
cbcr_src_h >>= 1;
|
||||
}
|
||||
|
||||
hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
|
||||
ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
|
||||
hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
|
||||
ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
|
||||
|
||||
val = vop2_scale_factor(src_w, dst_w);
|
||||
val = vop2_scale_factor(cbcr_src_w, dst_w);
|
||||
vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val);
|
||||
|
||||
val = vop2_scale_factor(src_h, dst_h);
|
||||
val = vop2_scale_factor(cbcr_src_h, dst_h);
|
||||
vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val);
|
||||
|
||||
vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4);
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <drm/display/drm_dp_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -214,20 +214,24 @@ sun4i_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret;
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->ddc_i2c ?: hdmi->i2c);
|
||||
if (!edid)
|
||||
drm_edid = drm_edid_read_ddc(connector, hdmi->ddc_i2c ?: hdmi->i2c);
|
||||
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
cec_s_phys_addr(hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
|
||||
if (!drm_edid)
|
||||
return 0;
|
||||
|
||||
DRM_DEBUG_DRIVER("Monitor is %s monitor\n",
|
||||
connector->display_info.is_hdmi ? "an HDMI" : "a DVI");
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test)
|
||||
DRM_BUDDY_RANGE_ALLOCATION),
|
||||
"buddy_alloc i failed with bias(%x-%x), size=%u, ps=%u\n",
|
||||
bias_start, bias_end, bias_size, bias_size);
|
||||
drm_buddy_free_list(&mm, &tmp);
|
||||
drm_buddy_free_list(&mm, &tmp, 0);
|
||||
|
||||
/* single page with internal round_up */
|
||||
KUNIT_ASSERT_FALSE_MSG(test,
|
||||
@ -113,7 +113,7 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test)
|
||||
DRM_BUDDY_RANGE_ALLOCATION),
|
||||
"buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
|
||||
bias_start, bias_end, ps, bias_size);
|
||||
drm_buddy_free_list(&mm, &tmp);
|
||||
drm_buddy_free_list(&mm, &tmp, 0);
|
||||
|
||||
/* random size within */
|
||||
size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps);
|
||||
@ -153,14 +153,14 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test)
|
||||
* unallocated, and ideally not always on the bias
|
||||
* boundaries.
|
||||
*/
|
||||
drm_buddy_free_list(&mm, &tmp);
|
||||
drm_buddy_free_list(&mm, &tmp, 0);
|
||||
} else {
|
||||
list_splice_tail(&tmp, &allocated);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(order);
|
||||
drm_buddy_free_list(&mm, &allocated);
|
||||
drm_buddy_free_list(&mm, &allocated, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
|
||||
/*
|
||||
@ -220,7 +220,149 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test)
|
||||
"buddy_alloc passed with bias(%x-%x), size=%u\n",
|
||||
bias_start, bias_end, ps);
|
||||
|
||||
drm_buddy_free_list(&mm, &allocated);
|
||||
drm_buddy_free_list(&mm, &allocated, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
static void drm_test_buddy_alloc_clear(struct kunit *test)
|
||||
{
|
||||
unsigned long n_pages, total, i = 0;
|
||||
DRM_RND_STATE(prng, random_seed);
|
||||
const unsigned long ps = SZ_4K;
|
||||
struct drm_buddy_block *block;
|
||||
const int max_order = 12;
|
||||
LIST_HEAD(allocated);
|
||||
struct drm_buddy mm;
|
||||
unsigned int order;
|
||||
u32 mm_size, size;
|
||||
LIST_HEAD(dirty);
|
||||
LIST_HEAD(clean);
|
||||
|
||||
mm_size = SZ_4K << max_order;
|
||||
KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps));
|
||||
|
||||
KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
|
||||
|
||||
/*
|
||||
* Idea is to allocate and free some random portion of the address space,
|
||||
* returning those pages as non-dirty and randomly alternate between
|
||||
* requesting dirty and non-dirty pages (not going over the limit
|
||||
* we freed as non-dirty), putting that into two separate lists.
|
||||
* Loop over both lists at the end checking that the dirty list
|
||||
* is indeed all dirty pages and vice versa. Free it all again,
|
||||
* keeping the dirty/clear status.
|
||||
*/
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
5 * ps, ps, &allocated,
|
||||
DRM_BUDDY_TOPDOWN_ALLOCATION),
|
||||
"buddy_alloc hit an error size=%lu\n", 5 * ps);
|
||||
drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED);
|
||||
|
||||
n_pages = 10;
|
||||
do {
|
||||
unsigned long flags;
|
||||
struct list_head *list;
|
||||
int slot = i % 2;
|
||||
|
||||
if (slot == 0) {
|
||||
list = &dirty;
|
||||
flags = 0;
|
||||
} else {
|
||||
list = &clean;
|
||||
flags = DRM_BUDDY_CLEAR_ALLOCATION;
|
||||
}
|
||||
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
ps, ps, list,
|
||||
flags),
|
||||
"buddy_alloc hit an error size=%lu\n", ps);
|
||||
} while (++i < n_pages);
|
||||
|
||||
list_for_each_entry(block, &clean, link)
|
||||
KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), true);
|
||||
|
||||
list_for_each_entry(block, &dirty, link)
|
||||
KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), false);
|
||||
|
||||
drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED);
|
||||
|
||||
/*
|
||||
* Trying to go over the clear limit for some allocation.
|
||||
* The allocation should never fail with reasonable page-size.
|
||||
*/
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
10 * ps, ps, &clean,
|
||||
DRM_BUDDY_CLEAR_ALLOCATION),
|
||||
"buddy_alloc hit an error size=%lu\n", 10 * ps);
|
||||
|
||||
drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED);
|
||||
drm_buddy_free_list(&mm, &dirty, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps));
|
||||
|
||||
/*
|
||||
* Create a new mm. Intentionally fragment the address space by creating
|
||||
* two alternating lists. Free both lists, one as dirty the other as clean.
|
||||
* Try to allocate double the previous size with matching min_page_size. The
|
||||
* allocation should never fail as it calls the force_merge. Also check that
|
||||
* the page is always dirty after force_merge. Free the page as dirty, then
|
||||
* repeat the whole thing, increment the order until we hit the max_order.
|
||||
*/
|
||||
|
||||
i = 0;
|
||||
n_pages = mm_size / ps;
|
||||
do {
|
||||
struct list_head *list;
|
||||
int slot = i % 2;
|
||||
|
||||
if (slot == 0)
|
||||
list = &dirty;
|
||||
else
|
||||
list = &clean;
|
||||
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
ps, ps, list, 0),
|
||||
"buddy_alloc hit an error size=%lu\n", ps);
|
||||
} while (++i < n_pages);
|
||||
|
||||
drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED);
|
||||
drm_buddy_free_list(&mm, &dirty, 0);
|
||||
|
||||
order = 1;
|
||||
do {
|
||||
size = SZ_4K << order;
|
||||
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
size, size, &allocated,
|
||||
DRM_BUDDY_CLEAR_ALLOCATION),
|
||||
"buddy_alloc hit an error size=%u\n", size);
|
||||
total = 0;
|
||||
list_for_each_entry(block, &allocated, link) {
|
||||
if (size != mm_size)
|
||||
KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), false);
|
||||
total += drm_buddy_block_size(&mm, block);
|
||||
}
|
||||
KUNIT_EXPECT_EQ(test, total, size);
|
||||
|
||||
drm_buddy_free_list(&mm, &allocated, 0);
|
||||
} while (++order <= max_order);
|
||||
|
||||
drm_buddy_fini(&mm);
|
||||
|
||||
/*
|
||||
* Create a new mm with a non power-of-two size. Allocate a random size, free as
|
||||
* cleared and then call fini. This will ensure the multi-root force merge during
|
||||
* fini.
|
||||
*/
|
||||
mm_size = 12 * SZ_4K;
|
||||
size = max(round_up(prandom_u32_state(&prng) % mm_size, ps), ps);
|
||||
KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps));
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
size, ps, &allocated,
|
||||
DRM_BUDDY_TOPDOWN_ALLOCATION),
|
||||
"buddy_alloc hit an error size=%u\n", size);
|
||||
drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -269,7 +411,7 @@ static void drm_test_buddy_alloc_contiguous(struct kunit *test)
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
"buddy_alloc didn't error size=%lu\n", 3 * ps);
|
||||
|
||||
drm_buddy_free_list(&mm, &middle);
|
||||
drm_buddy_free_list(&mm, &middle, 0);
|
||||
KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
3 * ps, ps, &allocated,
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
@ -279,7 +421,7 @@ static void drm_test_buddy_alloc_contiguous(struct kunit *test)
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
"buddy_alloc didn't error size=%lu\n", 2 * ps);
|
||||
|
||||
drm_buddy_free_list(&mm, &right);
|
||||
drm_buddy_free_list(&mm, &right, 0);
|
||||
KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
3 * ps, ps, &allocated,
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
@ -294,7 +436,7 @@ static void drm_test_buddy_alloc_contiguous(struct kunit *test)
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
"buddy_alloc hit an error size=%lu\n", 2 * ps);
|
||||
|
||||
drm_buddy_free_list(&mm, &left);
|
||||
drm_buddy_free_list(&mm, &left, 0);
|
||||
KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size,
|
||||
3 * ps, ps, &allocated,
|
||||
DRM_BUDDY_CONTIGUOUS_ALLOCATION),
|
||||
@ -306,7 +448,7 @@ static void drm_test_buddy_alloc_contiguous(struct kunit *test)
|
||||
|
||||
KUNIT_ASSERT_EQ(test, total, ps * 2 + ps * 3);
|
||||
|
||||
drm_buddy_free_list(&mm, &allocated);
|
||||
drm_buddy_free_list(&mm, &allocated, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -375,7 +517,7 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test)
|
||||
top, max_order);
|
||||
}
|
||||
|
||||
drm_buddy_free_list(&mm, &holes);
|
||||
drm_buddy_free_list(&mm, &holes, 0);
|
||||
|
||||
/* Nothing larger than blocks of chunk_size now available */
|
||||
for (order = 1; order <= max_order; order++) {
|
||||
@ -387,7 +529,7 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test)
|
||||
}
|
||||
|
||||
list_splice_tail(&holes, &blocks);
|
||||
drm_buddy_free_list(&mm, &blocks);
|
||||
drm_buddy_free_list(&mm, &blocks, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -482,7 +624,7 @@ static void drm_test_buddy_alloc_pessimistic(struct kunit *test)
|
||||
|
||||
list_del(&block->link);
|
||||
drm_buddy_free_block(&mm, block);
|
||||
drm_buddy_free_list(&mm, &blocks);
|
||||
drm_buddy_free_list(&mm, &blocks, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -528,7 +670,7 @@ static void drm_test_buddy_alloc_optimistic(struct kunit *test)
|
||||
size, size, &tmp, flags),
|
||||
"buddy_alloc unexpectedly succeeded, it should be full!");
|
||||
|
||||
drm_buddy_free_list(&mm, &blocks);
|
||||
drm_buddy_free_list(&mm, &blocks, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -563,7 +705,7 @@ static void drm_test_buddy_alloc_limit(struct kunit *test)
|
||||
drm_buddy_block_size(&mm, block),
|
||||
BIT_ULL(mm.max_order) * PAGE_SIZE);
|
||||
|
||||
drm_buddy_free_list(&mm, &allocated);
|
||||
drm_buddy_free_list(&mm, &allocated, 0);
|
||||
drm_buddy_fini(&mm);
|
||||
}
|
||||
|
||||
@ -584,6 +726,7 @@ static struct kunit_case drm_buddy_tests[] = {
|
||||
KUNIT_CASE(drm_test_buddy_alloc_pessimistic),
|
||||
KUNIT_CASE(drm_test_buddy_alloc_pathological),
|
||||
KUNIT_CASE(drm_test_buddy_alloc_contiguous),
|
||||
KUNIT_CASE(drm_test_buddy_alloc_clear),
|
||||
KUNIT_CASE(drm_test_buddy_alloc_range_bias),
|
||||
{}
|
||||
};
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "[TTM DEVICE] " fmt
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
|
@ -22,8 +22,9 @@
|
||||
* Authors: Christian König
|
||||
*/
|
||||
|
||||
#include <linux/iosys-map.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io-mapping.h>
|
||||
#include <linux/iosys-map.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
|
@ -32,10 +32,11 @@
|
||||
#define pr_fmt(fmt) "[TTM] " fmt
|
||||
|
||||
#include <linux/cc_platform.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_util.h>
|
||||
|
@ -115,14 +115,13 @@ v3d_open(struct drm_device *dev, struct drm_file *file)
|
||||
v3d_priv->v3d = v3d;
|
||||
|
||||
for (i = 0; i < V3D_MAX_QUEUES; i++) {
|
||||
v3d_priv->enabled_ns[i] = 0;
|
||||
v3d_priv->start_ns[i] = 0;
|
||||
v3d_priv->jobs_sent[i] = 0;
|
||||
|
||||
sched = &v3d->queue[i].sched;
|
||||
drm_sched_entity_init(&v3d_priv->sched_entity[i],
|
||||
DRM_SCHED_PRIORITY_NORMAL, &sched,
|
||||
1, NULL);
|
||||
|
||||
memset(&v3d_priv->stats[i], 0, sizeof(v3d_priv->stats[i]));
|
||||
seqcount_init(&v3d_priv->stats[i].lock);
|
||||
}
|
||||
|
||||
v3d_perfmon_open_file(v3d_priv);
|
||||
@ -144,6 +143,20 @@ v3d_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
kfree(v3d_priv);
|
||||
}
|
||||
|
||||
void v3d_get_stats(const struct v3d_stats *stats, u64 timestamp,
|
||||
u64 *active_runtime, u64 *jobs_completed)
|
||||
{
|
||||
unsigned int seq;
|
||||
|
||||
do {
|
||||
seq = read_seqcount_begin(&stats->lock);
|
||||
*active_runtime = stats->enabled_ns;
|
||||
if (stats->start_ns)
|
||||
*active_runtime += timestamp - stats->start_ns;
|
||||
*jobs_completed = stats->jobs_completed;
|
||||
} while (read_seqcount_retry(&stats->lock, seq));
|
||||
}
|
||||
|
||||
static void v3d_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
||||
{
|
||||
struct v3d_file_priv *file_priv = file->driver_priv;
|
||||
@ -151,20 +164,22 @@ static void v3d_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
||||
enum v3d_queue queue;
|
||||
|
||||
for (queue = 0; queue < V3D_MAX_QUEUES; queue++) {
|
||||
struct v3d_stats *stats = &file_priv->stats[queue];
|
||||
u64 active_runtime, jobs_completed;
|
||||
|
||||
v3d_get_stats(stats, timestamp, &active_runtime, &jobs_completed);
|
||||
|
||||
/* Note that, in case of a GPU reset, the time spent during an
|
||||
* attempt of executing the job is not computed in the runtime.
|
||||
*/
|
||||
drm_printf(p, "drm-engine-%s: \t%llu ns\n",
|
||||
v3d_queue_to_string(queue),
|
||||
file_priv->start_ns[queue] ? file_priv->enabled_ns[queue]
|
||||
+ timestamp - file_priv->start_ns[queue]
|
||||
: file_priv->enabled_ns[queue]);
|
||||
v3d_queue_to_string(queue), active_runtime);
|
||||
|
||||
/* Note that we only count jobs that completed. Therefore, jobs
|
||||
* that were resubmitted due to a GPU reset are not computed.
|
||||
*/
|
||||
drm_printf(p, "v3d-jobs-%s: \t%llu jobs\n",
|
||||
v3d_queue_to_string(queue), file_priv->jobs_sent[queue]);
|
||||
v3d_queue_to_string(queue), jobs_completed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,15 +36,27 @@ static inline char *v3d_queue_to_string(enum v3d_queue queue)
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
struct v3d_stats {
|
||||
u64 start_ns;
|
||||
u64 enabled_ns;
|
||||
u64 jobs_completed;
|
||||
|
||||
/*
|
||||
* This seqcount is used to protect the access to the GPU stats
|
||||
* variables. It must be used as, while we are reading the stats,
|
||||
* IRQs can happen and the stats can be updated.
|
||||
*/
|
||||
seqcount_t lock;
|
||||
};
|
||||
|
||||
struct v3d_queue_state {
|
||||
struct drm_gpu_scheduler sched;
|
||||
|
||||
u64 fence_context;
|
||||
u64 emit_seqno;
|
||||
|
||||
u64 start_ns;
|
||||
u64 enabled_ns;
|
||||
u64 jobs_sent;
|
||||
/* Stores the GPU stats for this queue in the global context. */
|
||||
struct v3d_stats stats;
|
||||
};
|
||||
|
||||
/* Performance monitor object. The perform lifetime is controlled by userspace
|
||||
@ -188,11 +200,8 @@ struct v3d_file_priv {
|
||||
|
||||
struct drm_sched_entity sched_entity[V3D_MAX_QUEUES];
|
||||
|
||||
u64 start_ns[V3D_MAX_QUEUES];
|
||||
|
||||
u64 enabled_ns[V3D_MAX_QUEUES];
|
||||
|
||||
u64 jobs_sent[V3D_MAX_QUEUES];
|
||||
/* Stores the GPU stats for a specific queue for this fd. */
|
||||
struct v3d_stats stats[V3D_MAX_QUEUES];
|
||||
};
|
||||
|
||||
struct v3d_bo {
|
||||
@ -508,6 +517,10 @@ struct drm_gem_object *v3d_prime_import_sg_table(struct drm_device *dev,
|
||||
/* v3d_debugfs.c */
|
||||
void v3d_debugfs_init(struct drm_minor *minor);
|
||||
|
||||
/* v3d_drv.c */
|
||||
void v3d_get_stats(const struct v3d_stats *stats, u64 timestamp,
|
||||
u64 *active_runtime, u64 *jobs_completed);
|
||||
|
||||
/* v3d_fence.c */
|
||||
extern const struct dma_fence_ops v3d_fence_ops;
|
||||
struct dma_fence *v3d_fence_create(struct v3d_dev *v3d, enum v3d_queue queue);
|
||||
@ -543,6 +556,7 @@ void v3d_mmu_insert_ptes(struct v3d_bo *bo);
|
||||
void v3d_mmu_remove_ptes(struct v3d_bo *bo);
|
||||
|
||||
/* v3d_sched.c */
|
||||
void v3d_job_update_stats(struct v3d_job *job, enum v3d_queue queue);
|
||||
int v3d_sched_init(struct v3d_dev *v3d);
|
||||
void v3d_sched_fini(struct v3d_dev *v3d);
|
||||
|
||||
|
@ -247,10 +247,11 @@ v3d_gem_init(struct drm_device *dev)
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < V3D_MAX_QUEUES; i++) {
|
||||
v3d->queue[i].fence_context = dma_fence_context_alloc(1);
|
||||
v3d->queue[i].start_ns = 0;
|
||||
v3d->queue[i].enabled_ns = 0;
|
||||
v3d->queue[i].jobs_sent = 0;
|
||||
struct v3d_queue_state *queue = &v3d->queue[i];
|
||||
|
||||
queue->fence_context = dma_fence_context_alloc(1);
|
||||
memset(&queue->stats, 0, sizeof(queue->stats));
|
||||
seqcount_init(&queue->stats.lock);
|
||||
}
|
||||
|
||||
spin_lock_init(&v3d->mm_lock);
|
||||
|
@ -102,18 +102,8 @@ v3d_irq(int irq, void *arg)
|
||||
if (intsts & V3D_INT_FLDONE) {
|
||||
struct v3d_fence *fence =
|
||||
to_v3d_fence(v3d->bin_job->base.irq_fence);
|
||||
struct v3d_file_priv *file = v3d->bin_job->base.file->driver_priv;
|
||||
u64 runtime = local_clock() - file->start_ns[V3D_BIN];
|
||||
|
||||
file->jobs_sent[V3D_BIN]++;
|
||||
v3d->queue[V3D_BIN].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_BIN] = 0;
|
||||
v3d->queue[V3D_BIN].start_ns = 0;
|
||||
|
||||
file->enabled_ns[V3D_BIN] += runtime;
|
||||
v3d->queue[V3D_BIN].enabled_ns += runtime;
|
||||
|
||||
v3d_job_update_stats(&v3d->bin_job->base, V3D_BIN);
|
||||
trace_v3d_bcl_irq(&v3d->drm, fence->seqno);
|
||||
dma_fence_signal(&fence->base);
|
||||
status = IRQ_HANDLED;
|
||||
@ -122,18 +112,8 @@ v3d_irq(int irq, void *arg)
|
||||
if (intsts & V3D_INT_FRDONE) {
|
||||
struct v3d_fence *fence =
|
||||
to_v3d_fence(v3d->render_job->base.irq_fence);
|
||||
struct v3d_file_priv *file = v3d->render_job->base.file->driver_priv;
|
||||
u64 runtime = local_clock() - file->start_ns[V3D_RENDER];
|
||||
|
||||
file->jobs_sent[V3D_RENDER]++;
|
||||
v3d->queue[V3D_RENDER].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_RENDER] = 0;
|
||||
v3d->queue[V3D_RENDER].start_ns = 0;
|
||||
|
||||
file->enabled_ns[V3D_RENDER] += runtime;
|
||||
v3d->queue[V3D_RENDER].enabled_ns += runtime;
|
||||
|
||||
v3d_job_update_stats(&v3d->render_job->base, V3D_RENDER);
|
||||
trace_v3d_rcl_irq(&v3d->drm, fence->seqno);
|
||||
dma_fence_signal(&fence->base);
|
||||
status = IRQ_HANDLED;
|
||||
@ -142,18 +122,8 @@ v3d_irq(int irq, void *arg)
|
||||
if (intsts & V3D_INT_CSDDONE(v3d->ver)) {
|
||||
struct v3d_fence *fence =
|
||||
to_v3d_fence(v3d->csd_job->base.irq_fence);
|
||||
struct v3d_file_priv *file = v3d->csd_job->base.file->driver_priv;
|
||||
u64 runtime = local_clock() - file->start_ns[V3D_CSD];
|
||||
|
||||
file->jobs_sent[V3D_CSD]++;
|
||||
v3d->queue[V3D_CSD].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_CSD] = 0;
|
||||
v3d->queue[V3D_CSD].start_ns = 0;
|
||||
|
||||
file->enabled_ns[V3D_CSD] += runtime;
|
||||
v3d->queue[V3D_CSD].enabled_ns += runtime;
|
||||
|
||||
v3d_job_update_stats(&v3d->csd_job->base, V3D_CSD);
|
||||
trace_v3d_csd_irq(&v3d->drm, fence->seqno);
|
||||
dma_fence_signal(&fence->base);
|
||||
status = IRQ_HANDLED;
|
||||
@ -189,18 +159,8 @@ v3d_hub_irq(int irq, void *arg)
|
||||
if (intsts & V3D_HUB_INT_TFUC) {
|
||||
struct v3d_fence *fence =
|
||||
to_v3d_fence(v3d->tfu_job->base.irq_fence);
|
||||
struct v3d_file_priv *file = v3d->tfu_job->base.file->driver_priv;
|
||||
u64 runtime = local_clock() - file->start_ns[V3D_TFU];
|
||||
|
||||
file->jobs_sent[V3D_TFU]++;
|
||||
v3d->queue[V3D_TFU].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_TFU] = 0;
|
||||
v3d->queue[V3D_TFU].start_ns = 0;
|
||||
|
||||
file->enabled_ns[V3D_TFU] += runtime;
|
||||
v3d->queue[V3D_TFU].enabled_ns += runtime;
|
||||
|
||||
v3d_job_update_stats(&v3d->tfu_job->base, V3D_TFU);
|
||||
trace_v3d_tfu_irq(&v3d->drm, fence->seqno);
|
||||
dma_fence_signal(&fence->base);
|
||||
status = IRQ_HANDLED;
|
||||
|
@ -105,11 +105,51 @@ v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job)
|
||||
v3d_perfmon_start(v3d, job->perfmon);
|
||||
}
|
||||
|
||||
static void
|
||||
v3d_job_start_stats(struct v3d_job *job, enum v3d_queue queue)
|
||||
{
|
||||
struct v3d_dev *v3d = job->v3d;
|
||||
struct v3d_file_priv *file = job->file->driver_priv;
|
||||
struct v3d_stats *global_stats = &v3d->queue[queue].stats;
|
||||
struct v3d_stats *local_stats = &file->stats[queue];
|
||||
u64 now = local_clock();
|
||||
|
||||
write_seqcount_begin(&local_stats->lock);
|
||||
local_stats->start_ns = now;
|
||||
write_seqcount_end(&local_stats->lock);
|
||||
|
||||
write_seqcount_begin(&global_stats->lock);
|
||||
global_stats->start_ns = now;
|
||||
write_seqcount_end(&global_stats->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
v3d_stats_update(struct v3d_stats *stats, u64 now)
|
||||
{
|
||||
write_seqcount_begin(&stats->lock);
|
||||
stats->enabled_ns += now - stats->start_ns;
|
||||
stats->jobs_completed++;
|
||||
stats->start_ns = 0;
|
||||
write_seqcount_end(&stats->lock);
|
||||
}
|
||||
|
||||
void
|
||||
v3d_job_update_stats(struct v3d_job *job, enum v3d_queue queue)
|
||||
{
|
||||
struct v3d_dev *v3d = job->v3d;
|
||||
struct v3d_file_priv *file = job->file->driver_priv;
|
||||
struct v3d_stats *global_stats = &v3d->queue[queue].stats;
|
||||
struct v3d_stats *local_stats = &file->stats[queue];
|
||||
u64 now = local_clock();
|
||||
|
||||
v3d_stats_update(local_stats, now);
|
||||
v3d_stats_update(global_stats, now);
|
||||
}
|
||||
|
||||
static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_bin_job *job = to_bin_job(sched_job);
|
||||
struct v3d_dev *v3d = job->base.v3d;
|
||||
struct v3d_file_priv *file = job->base.file->driver_priv;
|
||||
struct drm_device *dev = &v3d->drm;
|
||||
struct dma_fence *fence;
|
||||
unsigned long irqflags;
|
||||
@ -141,9 +181,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
|
||||
trace_v3d_submit_cl(dev, false, to_v3d_fence(fence)->seqno,
|
||||
job->start, job->end);
|
||||
|
||||
file->start_ns[V3D_BIN] = local_clock();
|
||||
v3d->queue[V3D_BIN].start_ns = file->start_ns[V3D_BIN];
|
||||
|
||||
v3d_job_start_stats(&job->base, V3D_BIN);
|
||||
v3d_switch_perfmon(v3d, &job->base);
|
||||
|
||||
/* Set the current and end address of the control list.
|
||||
@ -168,7 +206,6 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_render_job *job = to_render_job(sched_job);
|
||||
struct v3d_dev *v3d = job->base.v3d;
|
||||
struct v3d_file_priv *file = job->base.file->driver_priv;
|
||||
struct drm_device *dev = &v3d->drm;
|
||||
struct dma_fence *fence;
|
||||
|
||||
@ -196,9 +233,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
|
||||
trace_v3d_submit_cl(dev, true, to_v3d_fence(fence)->seqno,
|
||||
job->start, job->end);
|
||||
|
||||
file->start_ns[V3D_RENDER] = local_clock();
|
||||
v3d->queue[V3D_RENDER].start_ns = file->start_ns[V3D_RENDER];
|
||||
|
||||
v3d_job_start_stats(&job->base, V3D_RENDER);
|
||||
v3d_switch_perfmon(v3d, &job->base);
|
||||
|
||||
/* XXX: Set the QCFG */
|
||||
@ -217,7 +252,6 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_tfu_job *job = to_tfu_job(sched_job);
|
||||
struct v3d_dev *v3d = job->base.v3d;
|
||||
struct v3d_file_priv *file = job->base.file->driver_priv;
|
||||
struct drm_device *dev = &v3d->drm;
|
||||
struct dma_fence *fence;
|
||||
|
||||
@ -232,8 +266,7 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
|
||||
|
||||
trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno);
|
||||
|
||||
file->start_ns[V3D_TFU] = local_clock();
|
||||
v3d->queue[V3D_TFU].start_ns = file->start_ns[V3D_TFU];
|
||||
v3d_job_start_stats(&job->base, V3D_TFU);
|
||||
|
||||
V3D_WRITE(V3D_TFU_IIA(v3d->ver), job->args.iia);
|
||||
V3D_WRITE(V3D_TFU_IIS(v3d->ver), job->args.iis);
|
||||
@ -260,7 +293,6 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_csd_job *job = to_csd_job(sched_job);
|
||||
struct v3d_dev *v3d = job->base.v3d;
|
||||
struct v3d_file_priv *file = job->base.file->driver_priv;
|
||||
struct drm_device *dev = &v3d->drm;
|
||||
struct dma_fence *fence;
|
||||
int i, csd_cfg0_reg, csd_cfg_reg_count;
|
||||
@ -279,9 +311,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
|
||||
|
||||
trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno);
|
||||
|
||||
file->start_ns[V3D_CSD] = local_clock();
|
||||
v3d->queue[V3D_CSD].start_ns = file->start_ns[V3D_CSD];
|
||||
|
||||
v3d_job_start_stats(&job->base, V3D_CSD);
|
||||
v3d_switch_perfmon(v3d, &job->base);
|
||||
|
||||
csd_cfg0_reg = V3D_CSD_QUEUED_CFG0(v3d->ver);
|
||||
@ -530,8 +560,6 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_cpu_job *job = to_cpu_job(sched_job);
|
||||
struct v3d_dev *v3d = job->base.v3d;
|
||||
struct v3d_file_priv *file = job->base.file->driver_priv;
|
||||
u64 runtime;
|
||||
|
||||
v3d->cpu_job = job;
|
||||
|
||||
@ -540,25 +568,13 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file->start_ns[V3D_CPU] = local_clock();
|
||||
v3d->queue[V3D_CPU].start_ns = file->start_ns[V3D_CPU];
|
||||
|
||||
v3d_job_start_stats(&job->base, V3D_CPU);
|
||||
trace_v3d_cpu_job_begin(&v3d->drm, job->job_type);
|
||||
|
||||
cpu_job_function[job->job_type](job);
|
||||
|
||||
trace_v3d_cpu_job_end(&v3d->drm, job->job_type);
|
||||
|
||||
runtime = local_clock() - file->start_ns[V3D_CPU];
|
||||
|
||||
file->enabled_ns[V3D_CPU] += runtime;
|
||||
v3d->queue[V3D_CPU].enabled_ns += runtime;
|
||||
|
||||
file->jobs_sent[V3D_CPU]++;
|
||||
v3d->queue[V3D_CPU].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_CPU] = 0;
|
||||
v3d->queue[V3D_CPU].start_ns = 0;
|
||||
v3d_job_update_stats(&job->base, V3D_CPU);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -568,24 +584,12 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct v3d_job *job = to_v3d_job(sched_job);
|
||||
struct v3d_dev *v3d = job->v3d;
|
||||
struct v3d_file_priv *file = job->file->driver_priv;
|
||||
u64 runtime;
|
||||
|
||||
file->start_ns[V3D_CACHE_CLEAN] = local_clock();
|
||||
v3d->queue[V3D_CACHE_CLEAN].start_ns = file->start_ns[V3D_CACHE_CLEAN];
|
||||
v3d_job_start_stats(job, V3D_CACHE_CLEAN);
|
||||
|
||||
v3d_clean_caches(v3d);
|
||||
|
||||
runtime = local_clock() - file->start_ns[V3D_CACHE_CLEAN];
|
||||
|
||||
file->enabled_ns[V3D_CACHE_CLEAN] += runtime;
|
||||
v3d->queue[V3D_CACHE_CLEAN].enabled_ns += runtime;
|
||||
|
||||
file->jobs_sent[V3D_CACHE_CLEAN]++;
|
||||
v3d->queue[V3D_CACHE_CLEAN].jobs_sent++;
|
||||
|
||||
file->start_ns[V3D_CACHE_CLEAN] = 0;
|
||||
v3d->queue[V3D_CACHE_CLEAN].start_ns = 0;
|
||||
v3d_job_update_stats(job, V3D_CACHE_CLEAN);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -15,16 +15,15 @@ gpu_stats_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
struct v3d_dev *v3d = to_v3d_dev(drm);
|
||||
enum v3d_queue queue;
|
||||
u64 timestamp = local_clock();
|
||||
u64 active_runtime;
|
||||
ssize_t len = 0;
|
||||
|
||||
len += sysfs_emit(buf, "queue\ttimestamp\tjobs\truntime\n");
|
||||
|
||||
for (queue = 0; queue < V3D_MAX_QUEUES; queue++) {
|
||||
if (v3d->queue[queue].start_ns)
|
||||
active_runtime = timestamp - v3d->queue[queue].start_ns;
|
||||
else
|
||||
active_runtime = 0;
|
||||
struct v3d_stats *stats = &v3d->queue[queue].stats;
|
||||
u64 active_runtime, jobs_completed;
|
||||
|
||||
v3d_get_stats(stats, timestamp, &active_runtime, &jobs_completed);
|
||||
|
||||
/* Each line will display the queue name, timestamp, the number
|
||||
* of jobs sent to that queue and the runtime, as can be seem here:
|
||||
@ -38,9 +37,7 @@ gpu_stats_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
*/
|
||||
len += sysfs_emit_at(buf, len, "%s\t%llu\t%llu\t%llu\n",
|
||||
v3d_queue_to_string(queue),
|
||||
timestamp,
|
||||
v3d->queue[queue].jobs_sent,
|
||||
v3d->queue[queue].enabled_ns + active_runtime);
|
||||
timestamp, jobs_completed, active_runtime);
|
||||
}
|
||||
|
||||
return len;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef _VC4_DRV_H_
|
||||
#define _VC4_DRV_H_
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/refcount.h>
|
||||
|
@ -412,15 +412,14 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
struct drm_connector *connector = &vc4_hdmi->connector;
|
||||
struct edid *edid;
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* NOTE: This function should really be called with
|
||||
* vc4_hdmi->mutex held, but doing so results in reentrancy
|
||||
* issues since cec_s_phys_addr_from_edid might call
|
||||
* .adap_enable, which leads to that funtion being called with
|
||||
* our mutex held.
|
||||
* NOTE: This function should really be called with vc4_hdmi->mutex
|
||||
* held, but doing so results in reentrancy issues since
|
||||
* cec_s_phys_addr() might call .adap_enable, which leads to that
|
||||
* funtion being called with our mutex held.
|
||||
*
|
||||
* A similar situation occurs with vc4_hdmi_reset_link() that
|
||||
* will call into our KMS hooks if the scrambling was enabled.
|
||||
@ -435,12 +434,16 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
|
||||
return;
|
||||
}
|
||||
|
||||
edid = drm_get_edid(connector, vc4_hdmi->ddc);
|
||||
if (!edid)
|
||||
drm_edid = drm_edid_read_ddc(connector, vc4_hdmi->ddc);
|
||||
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
cec_s_phys_addr(vc4_hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
|
||||
if (!drm_edid)
|
||||
return;
|
||||
|
||||
cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
|
||||
kfree(edid);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
for (;;) {
|
||||
ret = vc4_hdmi_reset_link(connector, ctx);
|
||||
@ -492,28 +495,29 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret = 0;
|
||||
struct edid *edid;
|
||||
|
||||
/*
|
||||
* NOTE: This function should really take vc4_hdmi->mutex, but
|
||||
* doing so results in reentrancy issues since
|
||||
* cec_s_phys_addr_from_edid might call .adap_enable, which
|
||||
* leads to that funtion being called with our mutex held.
|
||||
* NOTE: This function should really take vc4_hdmi->mutex, but doing so
|
||||
* results in reentrancy issues since cec_s_phys_addr() might call
|
||||
* .adap_enable, which leads to that funtion being called with our mutex
|
||||
* held.
|
||||
*
|
||||
* Concurrency isn't an issue at the moment since we don't share
|
||||
* any state with any of the other frameworks so we can ignore
|
||||
* the lock for now.
|
||||
*/
|
||||
|
||||
edid = drm_get_edid(connector, vc4_hdmi->ddc);
|
||||
cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
|
||||
if (!edid)
|
||||
drm_edid = drm_edid_read_ddc(connector, vc4_hdmi->ddc);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
cec_s_phys_addr(vc4_hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
if (!drm_edid)
|
||||
return 0;
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
if (!vc4->hvs->vc5_hdmi_enable_hdmi_20) {
|
||||
struct drm_device *drm = connector->dev;
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "drm/drm_prime.h"
|
||||
#include "drm/drm_gem_ttm_helper.h"
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
static void vmw_gem_object_free(struct drm_gem_object *gobj)
|
||||
{
|
||||
struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gobj);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "xe_debugfs.h"
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include <drm/drm_debugfs.h>
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "xe_gt_debugfs.h"
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
|
@ -196,7 +196,7 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
|
||||
return 0;
|
||||
|
||||
error_free_blocks:
|
||||
drm_buddy_free_list(mm, &vres->blocks);
|
||||
drm_buddy_free_list(mm, &vres->blocks, 0);
|
||||
mutex_unlock(&mgr->lock);
|
||||
error_fini:
|
||||
ttm_resource_fini(man, &vres->base);
|
||||
@ -214,7 +214,7 @@ static void xe_ttm_vram_mgr_del(struct ttm_resource_manager *man,
|
||||
struct drm_buddy *mm = &mgr->mm;
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
drm_buddy_free_list(mm, &vres->blocks);
|
||||
drm_buddy_free_list(mm, &vres->blocks, 0);
|
||||
mgr->visible_avail += vres->used_visible_size;
|
||||
mutex_unlock(&mgr->lock);
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Copyright © 2022 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <drm/drm_debugfs.h>
|
||||
|
||||
#include "xe_gt.h"
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/dma/xilinx_dpdma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -64,15 +65,27 @@
|
||||
|
||||
#define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3
|
||||
|
||||
/**
|
||||
* enum zynqmp_dpsub_layer_mode - Layer mode
|
||||
* @ZYNQMP_DPSUB_LAYER_NONLIVE: non-live (memory) mode
|
||||
* @ZYNQMP_DPSUB_LAYER_LIVE: live (stream) mode
|
||||
*/
|
||||
enum zynqmp_dpsub_layer_mode {
|
||||
ZYNQMP_DPSUB_LAYER_NONLIVE,
|
||||
ZYNQMP_DPSUB_LAYER_LIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zynqmp_disp_format - Display subsystem format information
|
||||
* @drm_fmt: DRM format (4CC)
|
||||
* @bus_fmt: Media bus format
|
||||
* @buf_fmt: AV buffer format
|
||||
* @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats
|
||||
* @sf: Scaling factors for color components
|
||||
*/
|
||||
struct zynqmp_disp_format {
|
||||
u32 drm_fmt;
|
||||
u32 bus_fmt;
|
||||
u32 buf_fmt;
|
||||
bool swap;
|
||||
const u32 *sf;
|
||||
@ -172,6 +185,12 @@ static const u32 scaling_factors_565[] = {
|
||||
ZYNQMP_DISP_AV_BUF_5BIT_SF,
|
||||
};
|
||||
|
||||
static const u32 scaling_factors_666[] = {
|
||||
ZYNQMP_DISP_AV_BUF_6BIT_SF,
|
||||
ZYNQMP_DISP_AV_BUF_6BIT_SF,
|
||||
ZYNQMP_DISP_AV_BUF_6BIT_SF,
|
||||
};
|
||||
|
||||
static const u32 scaling_factors_888[] = {
|
||||
ZYNQMP_DISP_AV_BUF_8BIT_SF,
|
||||
ZYNQMP_DISP_AV_BUF_8BIT_SF,
|
||||
@ -354,6 +373,41 @@ static const struct zynqmp_disp_format avbuf_gfx_fmts[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/* List of live video layer formats */
|
||||
static const struct zynqmp_disp_format avbuf_live_fmts[] = {
|
||||
{
|
||||
.drm_fmt = DRM_FORMAT_RGB565,
|
||||
.bus_fmt = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 |
|
||||
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB,
|
||||
.sf = scaling_factors_666,
|
||||
}, {
|
||||
.drm_fmt = DRM_FORMAT_RGB888,
|
||||
.bus_fmt = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
|
||||
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB,
|
||||
.sf = scaling_factors_888,
|
||||
}, {
|
||||
.drm_fmt = DRM_FORMAT_YUV422,
|
||||
.bus_fmt = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
|
||||
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422,
|
||||
.sf = scaling_factors_888,
|
||||
}, {
|
||||
.drm_fmt = DRM_FORMAT_YUV444,
|
||||
.bus_fmt = MEDIA_BUS_FMT_VUY8_1X24,
|
||||
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
|
||||
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444,
|
||||
.sf = scaling_factors_888,
|
||||
}, {
|
||||
.drm_fmt = DRM_FORMAT_P210,
|
||||
.bus_fmt = MEDIA_BUS_FMT_UYVY10_1X20,
|
||||
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 |
|
||||
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422,
|
||||
.sf = scaling_factors_101010,
|
||||
},
|
||||
};
|
||||
|
||||
static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp *disp, int reg)
|
||||
{
|
||||
return readl(disp->avbuf.base + reg);
|
||||
@ -382,19 +436,29 @@ static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp *disp,
|
||||
const struct zynqmp_disp_format *fmt)
|
||||
{
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
u32 val, reg;
|
||||
|
||||
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT);
|
||||
val &= zynqmp_disp_layer_is_video(layer)
|
||||
? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK
|
||||
: ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK;
|
||||
val |= fmt->buf_fmt;
|
||||
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_FMT, val);
|
||||
layer->disp_fmt = fmt;
|
||||
if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) {
|
||||
reg = ZYNQMP_DISP_AV_BUF_FMT;
|
||||
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT);
|
||||
val &= zynqmp_disp_layer_is_video(layer)
|
||||
? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK
|
||||
: ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK;
|
||||
val |= fmt->buf_fmt;
|
||||
zynqmp_disp_avbuf_write(disp, reg, val);
|
||||
} else {
|
||||
reg = zynqmp_disp_layer_is_video(layer)
|
||||
? ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG
|
||||
: ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG;
|
||||
val = fmt->buf_fmt;
|
||||
zynqmp_disp_avbuf_write(disp, reg, val);
|
||||
}
|
||||
|
||||
for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) {
|
||||
unsigned int reg = zynqmp_disp_layer_is_video(layer)
|
||||
? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i)
|
||||
: ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i);
|
||||
reg = zynqmp_disp_layer_is_video(layer)
|
||||
? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i)
|
||||
: ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i);
|
||||
|
||||
zynqmp_disp_avbuf_write(disp, reg, fmt->sf[i]);
|
||||
}
|
||||
@ -872,11 +936,41 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_layer_find_live_format - Find format information for given
|
||||
* media bus format
|
||||
* @layer: The layer
|
||||
* @drm_fmt: Media bus format to search
|
||||
*
|
||||
* Search display subsystem format information corresponding to the given media
|
||||
* bus format @media_bus_format for the @layer, and return a pointer to the
|
||||
* format descriptor.
|
||||
*
|
||||
* Return: A pointer to the format descriptor if found, NULL otherwise
|
||||
*/
|
||||
static const struct zynqmp_disp_format *
|
||||
zynqmp_disp_layer_find_live_format(struct zynqmp_disp_layer *layer,
|
||||
u32 media_bus_format)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < layer->info->num_formats; i++)
|
||||
if (layer->info->formats[i].bus_fmt == media_bus_format)
|
||||
return &layer->info->formats[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_layer_drm_formats - Return the DRM formats supported by the layer
|
||||
* @layer: The layer
|
||||
* @num_formats: Pointer to the returned number of formats
|
||||
*
|
||||
* NOTE: This function doesn't make sense for live video layers and will
|
||||
* always return an empty list in such cases. zynqmp_disp_live_layer_formats()
|
||||
* should be used to query a list of media bus formats supported by the live
|
||||
* video input layer.
|
||||
*
|
||||
* Return: A newly allocated u32 array that stores all the DRM formats
|
||||
* supported by the layer. The number of formats in the array is returned
|
||||
* through the num_formats argument.
|
||||
@ -887,10 +981,17 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
|
||||
unsigned int i;
|
||||
u32 *formats;
|
||||
|
||||
if (WARN_ON(!layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)) {
|
||||
*num_formats = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
formats = kcalloc(layer->info->num_formats, sizeof(*formats),
|
||||
GFP_KERNEL);
|
||||
if (!formats)
|
||||
if (!formats) {
|
||||
*num_formats = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < layer->info->num_formats; ++i)
|
||||
formats[i] = layer->info->formats[i].drm_fmt;
|
||||
@ -899,18 +1000,52 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
|
||||
return formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_live_layer_formats - Return the media bus formats supported by
|
||||
* the live video layer
|
||||
* @layer: The layer
|
||||
* @num_formats: Pointer to the returned number of formats
|
||||
*
|
||||
* NOTE: This function should be used only for live video input layers.
|
||||
*
|
||||
* Return: A newly allocated u32 array of media bus formats supported by the
|
||||
* layer. The number of formats in the array is returned through the
|
||||
* @num_formats argument.
|
||||
*/
|
||||
u32 *zynqmp_disp_live_layer_formats(struct zynqmp_disp_layer *layer,
|
||||
unsigned int *num_formats)
|
||||
{
|
||||
unsigned int i;
|
||||
u32 *formats;
|
||||
|
||||
if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_LIVE)) {
|
||||
*num_formats = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
formats = kcalloc(layer->info->num_formats, sizeof(*formats),
|
||||
GFP_KERNEL);
|
||||
if (!formats) {
|
||||
*num_formats = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < layer->info->num_formats; ++i)
|
||||
formats[i] = layer->info->formats[i].bus_fmt;
|
||||
|
||||
*num_formats = layer->info->num_formats;
|
||||
return formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_layer_enable - Enable a layer
|
||||
* @layer: The layer
|
||||
* @mode: Operating mode of layer
|
||||
*
|
||||
* Enable the @layer in the audio/video buffer manager and the blender. DMA
|
||||
* channels are started separately by zynqmp_disp_layer_update().
|
||||
*/
|
||||
void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer,
|
||||
enum zynqmp_dpsub_layer_mode mode)
|
||||
void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
|
||||
{
|
||||
layer->mode = mode;
|
||||
zynqmp_disp_avbuf_enable_video(layer->disp, layer);
|
||||
zynqmp_disp_blend_layer_enable(layer->disp, layer);
|
||||
}
|
||||
@ -926,7 +1061,7 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (layer->disp->dpsub->dma_enabled) {
|
||||
if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) {
|
||||
for (i = 0; i < layer->drm_fmt->num_planes; i++)
|
||||
dmaengine_terminate_sync(layer->dmas[i].chan);
|
||||
}
|
||||
@ -940,6 +1075,9 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
|
||||
* @layer: The layer
|
||||
* @info: The format info
|
||||
*
|
||||
* NOTE: Use zynqmp_disp_layer_set_live_format() to set media bus format for
|
||||
* live video layers.
|
||||
*
|
||||
* Set the format for @layer to @info. The layer must be disabled.
|
||||
*/
|
||||
void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
|
||||
@ -947,14 +1085,16 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_NONLIVE))
|
||||
return;
|
||||
|
||||
layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format);
|
||||
if (WARN_ON(!layer->disp_fmt))
|
||||
return;
|
||||
layer->drm_fmt = info;
|
||||
|
||||
zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt);
|
||||
|
||||
if (!layer->disp->dpsub->dma_enabled)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set pconfig for each DMA channel to indicate they're part of a
|
||||
* video group.
|
||||
@ -974,6 +1114,32 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_layer_set_live_format - Set the live video layer format
|
||||
* @layer: The layer
|
||||
* @info: The format info
|
||||
*
|
||||
* NOTE: This function should not be used to set format for non-live video
|
||||
* layer. Use zynqmp_disp_layer_set_format() instead.
|
||||
*
|
||||
* Set the display format for the live @layer. The layer must be disabled.
|
||||
*/
|
||||
void zynqmp_disp_layer_set_live_format(struct zynqmp_disp_layer *layer,
|
||||
u32 media_bus_format)
|
||||
{
|
||||
if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_LIVE))
|
||||
return;
|
||||
|
||||
layer->disp_fmt = zynqmp_disp_layer_find_live_format(layer,
|
||||
media_bus_format);
|
||||
if (WARN_ON(!layer->disp_fmt))
|
||||
return;
|
||||
|
||||
zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt);
|
||||
|
||||
layer->drm_fmt = drm_format_info(layer->disp_fmt->drm_fmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* zynqmp_disp_layer_update - Update the layer framebuffer
|
||||
* @layer: The layer
|
||||
@ -990,7 +1156,7 @@ int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
|
||||
const struct drm_format_info *info = layer->drm_fmt;
|
||||
unsigned int i;
|
||||
|
||||
if (!layer->disp->dpsub->dma_enabled)
|
||||
if (layer->mode == ZYNQMP_DPSUB_LAYER_LIVE)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < info->num_planes; i++) {
|
||||
@ -1040,9 +1206,6 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp,
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!layer->info || !disp->dpsub->dma_enabled)
|
||||
return;
|
||||
|
||||
for (i = 0; i < layer->info->num_channels; i++) {
|
||||
struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
|
||||
|
||||
@ -1083,9 +1246,6 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!disp->dpsub->dma_enabled)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < layer->info->num_channels; i++) {
|
||||
struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
|
||||
char dma_channel_name[16];
|
||||
@ -1124,6 +1284,11 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
|
||||
.num_channels = 1,
|
||||
},
|
||||
};
|
||||
static const struct zynqmp_disp_layer_info live_layer_info = {
|
||||
.formats = avbuf_live_fmts,
|
||||
.num_formats = ARRAY_SIZE(avbuf_live_fmts),
|
||||
.num_channels = 0,
|
||||
};
|
||||
|
||||
unsigned int i;
|
||||
int ret;
|
||||
@ -1133,7 +1298,17 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
|
||||
|
||||
layer->id = i;
|
||||
layer->disp = disp;
|
||||
layer->info = &layer_info[i];
|
||||
/*
|
||||
* For now assume dpsub works in either live or non-live mode for both layers.
|
||||
* Hybrid mode is not supported yet.
|
||||
*/
|
||||
if (disp->dpsub->dma_enabled) {
|
||||
layer->mode = ZYNQMP_DPSUB_LAYER_NONLIVE;
|
||||
layer->info = &layer_info[i];
|
||||
} else {
|
||||
layer->mode = ZYNQMP_DPSUB_LAYER_LIVE;
|
||||
layer->info = &live_layer_info;
|
||||
}
|
||||
|
||||
ret = zynqmp_disp_layer_request_dma(disp, layer);
|
||||
if (ret)
|
||||
|
@ -42,16 +42,6 @@ enum zynqmp_dpsub_layer_id {
|
||||
ZYNQMP_DPSUB_LAYER_GFX,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum zynqmp_dpsub_layer_mode - Layer mode
|
||||
* @ZYNQMP_DPSUB_LAYER_NONLIVE: non-live (memory) mode
|
||||
* @ZYNQMP_DPSUB_LAYER_LIVE: live (stream) mode
|
||||
*/
|
||||
enum zynqmp_dpsub_layer_mode {
|
||||
ZYNQMP_DPSUB_LAYER_NONLIVE,
|
||||
ZYNQMP_DPSUB_LAYER_LIVE,
|
||||
};
|
||||
|
||||
void zynqmp_disp_enable(struct zynqmp_disp *disp);
|
||||
void zynqmp_disp_disable(struct zynqmp_disp *disp);
|
||||
int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
|
||||
@ -62,11 +52,14 @@ void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
|
||||
|
||||
u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
|
||||
unsigned int *num_formats);
|
||||
void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer,
|
||||
enum zynqmp_dpsub_layer_mode mode);
|
||||
u32 *zynqmp_disp_live_layer_formats(struct zynqmp_disp_layer *layer,
|
||||
unsigned int *num_formats);
|
||||
void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer);
|
||||
void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer);
|
||||
void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
|
||||
const struct drm_format_info *info);
|
||||
void zynqmp_disp_layer_set_live_format(struct zynqmp_disp_layer *layer,
|
||||
u32 media_bus_format);
|
||||
int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
|
||||
struct drm_plane_state *state);
|
||||
|
||||
|
@ -165,10 +165,10 @@
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK GENMASK(2, 0)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB (0x0 << 4)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 (0x1 << 4)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 (0x2 << 4)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY (0x3 << 4)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK GENMASK(5, 4)
|
||||
#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8)
|
||||
#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -1276,28 +1277,45 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
|
||||
* DISP Configuration
|
||||
*/
|
||||
|
||||
/**
|
||||
* zynqmp_dp_disp_connected_live_layer - Return the first connected live layer
|
||||
* @dp: DisplayPort IP core structure
|
||||
*
|
||||
* Return: The first connected live display layer or NULL if none of the live
|
||||
* layers are connected.
|
||||
*/
|
||||
static struct zynqmp_disp_layer *
|
||||
zynqmp_dp_disp_connected_live_layer(struct zynqmp_dp *dp)
|
||||
{
|
||||
if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
|
||||
return dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_VID];
|
||||
else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
|
||||
return dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void zynqmp_dp_disp_enable(struct zynqmp_dp *dp,
|
||||
struct drm_bridge_state *old_bridge_state)
|
||||
{
|
||||
enum zynqmp_dpsub_layer_id layer_id;
|
||||
struct zynqmp_disp_layer *layer;
|
||||
const struct drm_format_info *info;
|
||||
struct drm_bridge_state *bridge_state;
|
||||
u32 bus_fmt;
|
||||
|
||||
if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
|
||||
layer_id = ZYNQMP_DPSUB_LAYER_VID;
|
||||
else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
|
||||
layer_id = ZYNQMP_DPSUB_LAYER_GFX;
|
||||
else
|
||||
layer = zynqmp_dp_disp_connected_live_layer(dp);
|
||||
if (!layer)
|
||||
return;
|
||||
|
||||
layer = dp->dpsub->layers[layer_id];
|
||||
bridge_state = drm_atomic_get_new_bridge_state(old_bridge_state->base.state,
|
||||
old_bridge_state->bridge);
|
||||
if (WARN_ON(!bridge_state))
|
||||
return;
|
||||
|
||||
/* TODO: Make the format configurable. */
|
||||
info = drm_format_info(DRM_FORMAT_YUV422);
|
||||
zynqmp_disp_layer_set_format(layer, info);
|
||||
zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_LIVE);
|
||||
bus_fmt = bridge_state->input_bus_cfg.format;
|
||||
zynqmp_disp_layer_set_live_format(layer, bus_fmt);
|
||||
zynqmp_disp_layer_enable(layer);
|
||||
|
||||
if (layer_id == ZYNQMP_DPSUB_LAYER_GFX)
|
||||
if (layer == dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX])
|
||||
zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, true, 255);
|
||||
else
|
||||
zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, false, 0);
|
||||
@ -1310,11 +1328,8 @@ static void zynqmp_dp_disp_disable(struct zynqmp_dp *dp,
|
||||
{
|
||||
struct zynqmp_disp_layer *layer;
|
||||
|
||||
if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
|
||||
layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_VID];
|
||||
else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
|
||||
layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX];
|
||||
else
|
||||
layer = zynqmp_dp_disp_connected_live_layer(dp);
|
||||
if (!layer)
|
||||
return;
|
||||
|
||||
zynqmp_disp_disable(dp->dpsub->disp);
|
||||
@ -1568,6 +1583,35 @@ static const struct drm_edid *zynqmp_dp_bridge_edid_read(struct drm_bridge *brid
|
||||
return drm_edid_read_ddc(connector, &dp->aux.ddc);
|
||||
}
|
||||
|
||||
static u32 *zynqmp_dp_bridge_default_bus_fmts(unsigned int *num_input_fmts)
|
||||
{
|
||||
u32 *formats = kzalloc(sizeof(*formats), GFP_KERNEL);
|
||||
|
||||
if (formats)
|
||||
*formats = MEDIA_BUS_FMT_FIXED;
|
||||
*num_input_fmts = !!formats;
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
static u32 *
|
||||
zynqmp_dp_bridge_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)
|
||||
{
|
||||
struct zynqmp_dp *dp = bridge_to_dp(bridge);
|
||||
struct zynqmp_disp_layer *layer;
|
||||
|
||||
layer = zynqmp_dp_disp_connected_live_layer(dp);
|
||||
if (layer)
|
||||
return zynqmp_disp_live_layer_formats(layer, num_input_fmts);
|
||||
else
|
||||
return zynqmp_dp_bridge_default_bus_fmts(num_input_fmts);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
|
||||
.attach = zynqmp_dp_bridge_attach,
|
||||
.detach = zynqmp_dp_bridge_detach,
|
||||
@ -1580,6 +1624,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
|
||||
.atomic_check = zynqmp_dp_bridge_atomic_check,
|
||||
.detect = zynqmp_dp_bridge_detect,
|
||||
.edid_read = zynqmp_dp_bridge_edid_read,
|
||||
.atomic_get_input_bus_fmts = zynqmp_dp_bridge_get_input_bus_fmts,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
@ -122,7 +122,7 @@ static void zynqmp_dpsub_plane_atomic_update(struct drm_plane *plane,
|
||||
|
||||
/* Enable or re-enable the plane if the format has changed. */
|
||||
if (format_changed)
|
||||
zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_NONLIVE);
|
||||
zynqmp_disp_layer_enable(layer);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs zynqmp_dpsub_plane_helper_funcs = {
|
||||
|
@ -25,6 +25,8 @@
|
||||
#define DRM_BUDDY_RANGE_ALLOCATION BIT(0)
|
||||
#define DRM_BUDDY_TOPDOWN_ALLOCATION BIT(1)
|
||||
#define DRM_BUDDY_CONTIGUOUS_ALLOCATION BIT(2)
|
||||
#define DRM_BUDDY_CLEAR_ALLOCATION BIT(3)
|
||||
#define DRM_BUDDY_CLEARED BIT(4)
|
||||
|
||||
struct drm_buddy_block {
|
||||
#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
|
||||
@ -32,8 +34,9 @@ struct drm_buddy_block {
|
||||
#define DRM_BUDDY_ALLOCATED (1 << 10)
|
||||
#define DRM_BUDDY_FREE (2 << 10)
|
||||
#define DRM_BUDDY_SPLIT (3 << 10)
|
||||
#define DRM_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9)
|
||||
/* Free to be used, if needed in the future */
|
||||
#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
|
||||
#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6)
|
||||
#define DRM_BUDDY_HEADER_ORDER GENMASK_ULL(5, 0)
|
||||
u64 header;
|
||||
|
||||
@ -86,6 +89,7 @@ struct drm_buddy {
|
||||
u64 chunk_size;
|
||||
u64 size;
|
||||
u64 avail;
|
||||
u64 clear_avail;
|
||||
};
|
||||
|
||||
static inline u64
|
||||
@ -112,6 +116,12 @@ drm_buddy_block_is_allocated(struct drm_buddy_block *block)
|
||||
return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
drm_buddy_block_is_clear(struct drm_buddy_block *block)
|
||||
{
|
||||
return block->header & DRM_BUDDY_HEADER_CLEAR;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
drm_buddy_block_is_free(struct drm_buddy_block *block)
|
||||
{
|
||||
@ -150,7 +160,9 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
|
||||
|
||||
void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block);
|
||||
|
||||
void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects);
|
||||
void drm_buddy_free_list(struct drm_buddy *mm,
|
||||
struct list_head *objects,
|
||||
unsigned int flags);
|
||||
|
||||
void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
|
||||
void drm_buddy_block_print(struct drm_buddy *mm,
|
||||
|
@ -953,7 +953,7 @@ struct drm_mode_config {
|
||||
struct drm_property *modifiers_property;
|
||||
|
||||
/**
|
||||
* @size_hints_propertty: Plane SIZE_HINTS property.
|
||||
* @size_hints_property: Plane SIZE_HINTS property.
|
||||
*/
|
||||
struct drm_property *size_hints_property;
|
||||
|
||||
|
@ -28,14 +28,14 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dynamic_debug.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
|
||||
struct debugfs_regset32;
|
||||
struct drm_device;
|
||||
struct seq_file;
|
||||
|
||||
/* Do *not* use outside of drm_print.[ch]! */
|
||||
extern unsigned long __drm_debug;
|
||||
|
Loading…
Reference in New Issue
Block a user