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