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:
Dave Airlie 2024-04-26 13:29:03 +10:00
commit 90153b3666
96 changed files with 2651 additions and 1042 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@
*
*/
#include <linux/debugfs.h>
#include <linux/firmware.h>
#include "i915_drv.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/pci.h>

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

View File

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

View File

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

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/iosys-map.h>
#include <linux/pci.h>

View File

@ -27,6 +27,8 @@
* Christian König
*/
#include <linux/debugfs.h>
#include <drm/drm_file.h>
#include "radeon.h"

View File

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

View File

@ -27,6 +27,8 @@
* Christian König
*/
#include <linux/debugfs.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>

View File

@ -30,6 +30,7 @@
* Dave Airlie
*/
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/pagemap.h>
#include <linux/pci.h>

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

View File

@ -26,6 +26,7 @@
* Jerome Glisse
*/
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@
#include "xe_debugfs.h"
#include <linux/debugfs.h>
#include <linux/string_helpers.h>
#include <drm/drm_debugfs.h>

View File

@ -5,6 +5,8 @@
#include "xe_gt_debugfs.h"
#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>

View File

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

View File

@ -3,6 +3,8 @@
* Copyright © 2022 Intel Corporation
*/
#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
#include "xe_gt.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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