forked from Minki/linux
Merge tag 'drm-msm-next-2022-05-09' of https://gitlab.freedesktop.org/drm/msm into drm-next
- Fourcc modifier for tiled but not compressed layouts - Support for userspace allocated IOVA (GPU virtual address) - Devfreq clamp_to_idle fix - DPU: DSC (Display Stream Compression) support - DPU: inline rotation support on SC7280 - DPU: update DP timings to follow vendor recommendations - DP, DPU: add support for wide bus (on newer chipsets) - DP: eDP support - Merge DPU1 and MDP5 MDSS driver, make dpu/mdp device the master component - MDSS: optionally reset the IP block at the bootup to drop bootloader state - Properly register and unregister internal bridges in the DRM framework - Complete DPU IRQ cleanup - DP: conversion to use drm_bridge and drm_bridge_connector - eDP: drop old eDP parts again - DPU: writeback support - Misc small fixes Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rob Clark <robdclark@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGvJCr_1D8d0dgmyQC5HD4gmXeZw=bFV_CNCfceZbpMxRw@mail.gmail.com
This commit is contained in:
commit
f83493f7d3
@ -66,6 +66,10 @@ properties:
|
||||
interconnect-names:
|
||||
const: mdp0-mem
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: MDSS_CORE reset
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
|
@ -65,6 +65,10 @@ properties:
|
||||
interconnect-names:
|
||||
const: mdp0-mem
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: MDSS_CORE reset
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
|
@ -64,6 +64,10 @@ properties:
|
||||
interconnect-names:
|
||||
const: mdp0-mem
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: MDSS_CORE reset
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
|
@ -57,6 +57,10 @@ properties:
|
||||
|
||||
ranges: true
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: MDSS_CORE reset
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
|
@ -6258,8 +6258,9 @@ F: drivers/gpu/drm/tiny/panel-mipi-dbi.c
|
||||
|
||||
DRM DRIVER FOR MSM ADRENO GPU
|
||||
M: Rob Clark <robdclark@gmail.com>
|
||||
M: Sean Paul <sean@poorly.run>
|
||||
R: Abhinav Kumar <quic_abhinavk@quicinc.com>
|
||||
M: Abhinav Kumar <quic_abhinavk@quicinc.com>
|
||||
M: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
|
||||
R: Sean Paul <sean@poorly.run>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: freedreno@lists.freedesktop.org
|
||||
|
@ -155,7 +155,6 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
|
||||
kwb_conn->wb_layer = kcrtc->master->wb_layer;
|
||||
|
||||
wb_conn = &kwb_conn->base;
|
||||
wb_conn->encoder.possible_crtcs = BIT(drm_crtc_index(&kcrtc->base));
|
||||
|
||||
formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
|
||||
kwb_conn->wb_layer->layer_type,
|
||||
@ -164,7 +163,8 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
|
||||
err = drm_writeback_connector_init(&kms->base, wb_conn,
|
||||
&komeda_wb_connector_funcs,
|
||||
&komeda_wb_encoder_helper_funcs,
|
||||
formats, n_formats);
|
||||
formats, n_formats,
|
||||
BIT(drm_crtc_index(&kcrtc->base)));
|
||||
komeda_put_fourcc_list(formats);
|
||||
if (err) {
|
||||
kfree(kwb_conn);
|
||||
|
@ -212,7 +212,6 @@ int malidp_mw_connector_init(struct drm_device *drm)
|
||||
if (!malidp->dev->hw->enable_memwrite)
|
||||
return 0;
|
||||
|
||||
malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
|
||||
drm_connector_helper_add(&malidp->mw_connector.base,
|
||||
&malidp_mw_connector_helper_funcs);
|
||||
|
||||
@ -223,7 +222,8 @@ int malidp_mw_connector_init(struct drm_device *drm)
|
||||
ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
|
||||
&malidp_mw_connector_funcs,
|
||||
&malidp_mw_encoder_helper_funcs,
|
||||
formats, n_formats);
|
||||
formats, n_formats,
|
||||
1 << drm_crtc_index(&malidp->crtc));
|
||||
kfree(formats);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -157,6 +157,7 @@ static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
|
||||
* @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
|
||||
* @formats: Array of supported pixel formats for the writeback engine
|
||||
* @n_formats: Length of the formats array
|
||||
* @possible_crtcs: possible crtcs for the internal writeback encoder
|
||||
*
|
||||
* This function creates the writeback-connector-specific properties if they
|
||||
* have not been already created, initializes the connector as
|
||||
@ -174,7 +175,64 @@ int drm_writeback_connector_init(struct drm_device *dev,
|
||||
struct drm_writeback_connector *wb_connector,
|
||||
const struct drm_connector_funcs *con_funcs,
|
||||
const struct drm_encoder_helper_funcs *enc_helper_funcs,
|
||||
const u32 *formats, int n_formats)
|
||||
const u32 *formats, int n_formats,
|
||||
u32 possible_crtcs)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
|
||||
|
||||
wb_connector->encoder.possible_crtcs = possible_crtcs;
|
||||
|
||||
ret = drm_encoder_init(dev, &wb_connector->encoder,
|
||||
&drm_writeback_encoder_funcs,
|
||||
DRM_MODE_ENCODER_VIRTUAL, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_writeback_connector_init_with_encoder(dev, wb_connector, &wb_connector->encoder,
|
||||
con_funcs, formats, n_formats);
|
||||
|
||||
if (ret)
|
||||
drm_encoder_cleanup(&wb_connector->encoder);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_writeback_connector_init);
|
||||
|
||||
/**
|
||||
* drm_writeback_connector_init_with_encoder - Initialize a writeback connector with
|
||||
* a custom encoder
|
||||
*
|
||||
* @dev: DRM device
|
||||
* @wb_connector: Writeback connector to initialize
|
||||
* @enc: handle to the already initialized drm encoder
|
||||
* @con_funcs: Connector funcs vtable
|
||||
* @formats: Array of supported pixel formats for the writeback engine
|
||||
* @n_formats: Length of the formats array
|
||||
*
|
||||
* This function creates the writeback-connector-specific properties if they
|
||||
* have not been already created, initializes the connector as
|
||||
* type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
|
||||
* values.
|
||||
*
|
||||
* This function assumes that the drm_writeback_connector's encoder has already been
|
||||
* created and initialized before invoking this function.
|
||||
*
|
||||
* In addition, this function also assumes that callers of this API will manage
|
||||
* assigning the encoder helper functions, possible_crtcs and any other encoder
|
||||
* specific operation.
|
||||
*
|
||||
* Drivers should always use this function instead of drm_connector_init() to
|
||||
* set up writeback connectors if they want to manage themselves the lifetime of the
|
||||
* associated encoder.
|
||||
*
|
||||
* Returns: 0 on success, or a negative error code
|
||||
*/
|
||||
int drm_writeback_connector_init_with_encoder(struct drm_device *dev,
|
||||
struct drm_writeback_connector *wb_connector, struct drm_encoder *enc,
|
||||
const struct drm_connector_funcs *con_funcs, const u32 *formats,
|
||||
int n_formats)
|
||||
{
|
||||
struct drm_property_blob *blob;
|
||||
struct drm_connector *connector = &wb_connector->base;
|
||||
@ -189,12 +247,6 @@ int drm_writeback_connector_init(struct drm_device *dev,
|
||||
if (IS_ERR(blob))
|
||||
return PTR_ERR(blob);
|
||||
|
||||
drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
|
||||
ret = drm_encoder_init(dev, &wb_connector->encoder,
|
||||
&drm_writeback_encoder_funcs,
|
||||
DRM_MODE_ENCODER_VIRTUAL, NULL);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
connector->interlace_allowed = 0;
|
||||
|
||||
@ -203,8 +255,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
|
||||
if (ret)
|
||||
goto connector_fail;
|
||||
|
||||
ret = drm_connector_attach_encoder(connector,
|
||||
&wb_connector->encoder);
|
||||
ret = drm_connector_attach_encoder(connector, enc);
|
||||
if (ret)
|
||||
goto attach_fail;
|
||||
|
||||
@ -233,12 +284,10 @@ int drm_writeback_connector_init(struct drm_device *dev,
|
||||
attach_fail:
|
||||
drm_connector_cleanup(connector);
|
||||
connector_fail:
|
||||
drm_encoder_cleanup(&wb_connector->encoder);
|
||||
fail:
|
||||
drm_property_blob_put(blob);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_writeback_connector_init);
|
||||
EXPORT_SYMBOL(drm_writeback_connector_init_with_encoder);
|
||||
|
||||
int drm_writeback_set_fb(struct drm_connector_state *conn_state,
|
||||
struct drm_framebuffer *fb)
|
||||
|
@ -12,6 +12,7 @@ config DRM_MSM
|
||||
select IOMMU_IO_PGTABLE
|
||||
select QCOM_MDT_LOADER if ARCH_QCOM
|
||||
select REGULATOR
|
||||
select DRM_DP_AUX_BUS
|
||||
select DRM_DISPLAY_DP_HELPER
|
||||
select DRM_DISPLAY_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
@ -48,12 +49,39 @@ config DRM_MSM_GPU_SUDO
|
||||
Only use this if you are a driver developer. This should *not*
|
||||
be enabled for production kernels. If unsure, say N.
|
||||
|
||||
config DRM_MSM_HDMI_HDCP
|
||||
bool "Enable HDMI HDCP support in MSM DRM driver"
|
||||
config DRM_MSM_MDSS
|
||||
bool
|
||||
depends on DRM_MSM
|
||||
default n
|
||||
|
||||
config DRM_MSM_MDP4
|
||||
bool "Enable MDP4 support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
default y
|
||||
help
|
||||
Choose this option to enable HDCP state machine
|
||||
Compile in support for the Mobile Display Processor v4 (MDP4) in
|
||||
the MSM DRM driver. It is the older display controller found in
|
||||
devices using APQ8064/MSM8960/MSM8x60 platforms.
|
||||
|
||||
config DRM_MSM_MDP5
|
||||
bool "Enable MDP5 support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
select DRM_MSM_MDSS
|
||||
default y
|
||||
help
|
||||
Compile in support for the Mobile Display Processor v5 (MDP5) in
|
||||
the MSM DRM driver. It is the display controller found in devices
|
||||
using e.g. APQ8016/MSM8916/APQ8096/MSM8996/MSM8974/SDM6x0 platforms.
|
||||
|
||||
config DRM_MSM_DPU
|
||||
bool "Enable DPU support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
select DRM_MSM_MDSS
|
||||
default y
|
||||
help
|
||||
Compile in support for the Display Processing Unit in
|
||||
the MSM DRM driver. It is the display controller found in devices
|
||||
using e.g. SDM845 and newer platforms.
|
||||
|
||||
config DRM_MSM_DP
|
||||
bool "Enable DisplayPort support in MSM DRM driver"
|
||||
@ -118,3 +146,20 @@ config DRM_MSM_DSI_7NM_PHY
|
||||
help
|
||||
Choose this option if DSI PHY on SM8150/SM8250/SC7280 is used on
|
||||
the platform.
|
||||
|
||||
config DRM_MSM_HDMI
|
||||
bool "Enable HDMI support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
default y
|
||||
help
|
||||
Compile in support for the HDMI output MSM DRM driver. It can
|
||||
be a primary or a secondary display on device. Note that this is used
|
||||
only for the direct HDMI output. If the device outputs HDMI data
|
||||
throught some kind of DSI-to-HDMI bridge, this option can be disabled.
|
||||
|
||||
config DRM_MSM_HDMI_HDCP
|
||||
bool "Enable HDMI HDCP support in MSM DRM driver"
|
||||
depends on DRM_MSM && DRM_MSM_HDMI
|
||||
default y
|
||||
help
|
||||
Choose this option to enable HDCP state machine
|
||||
|
@ -16,6 +16,8 @@ msm-y := \
|
||||
adreno/a6xx_gpu.o \
|
||||
adreno/a6xx_gmu.o \
|
||||
adreno/a6xx_hfi.o \
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_HDMI) += \
|
||||
hdmi/hdmi.o \
|
||||
hdmi/hdmi_audio.o \
|
||||
hdmi/hdmi_bridge.o \
|
||||
@ -27,9 +29,10 @@ msm-y := \
|
||||
hdmi/hdmi_phy_8x60.o \
|
||||
hdmi/hdmi_phy_8x74.o \
|
||||
hdmi/hdmi_pll_8960.o \
|
||||
disp/mdp_format.o \
|
||||
disp/mdp_kms.o \
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_MDP4) += \
|
||||
disp/mdp4/mdp4_crtc.o \
|
||||
disp/mdp4/mdp4_dsi_encoder.o \
|
||||
disp/mdp4/mdp4_dtv_encoder.o \
|
||||
disp/mdp4/mdp4_lcdc_encoder.o \
|
||||
disp/mdp4/mdp4_lvds_connector.o \
|
||||
@ -37,25 +40,31 @@ msm-y := \
|
||||
disp/mdp4/mdp4_irq.o \
|
||||
disp/mdp4/mdp4_kms.o \
|
||||
disp/mdp4/mdp4_plane.o \
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_MDP5) += \
|
||||
disp/mdp5/mdp5_cfg.o \
|
||||
disp/mdp5/mdp5_cmd_encoder.o \
|
||||
disp/mdp5/mdp5_ctl.o \
|
||||
disp/mdp5/mdp5_crtc.o \
|
||||
disp/mdp5/mdp5_encoder.o \
|
||||
disp/mdp5/mdp5_irq.o \
|
||||
disp/mdp5/mdp5_mdss.o \
|
||||
disp/mdp5/mdp5_kms.o \
|
||||
disp/mdp5/mdp5_pipe.o \
|
||||
disp/mdp5/mdp5_mixer.o \
|
||||
disp/mdp5/mdp5_plane.o \
|
||||
disp/mdp5/mdp5_smp.o \
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_DPU) += \
|
||||
disp/dpu1/dpu_core_perf.o \
|
||||
disp/dpu1/dpu_crtc.o \
|
||||
disp/dpu1/dpu_encoder.o \
|
||||
disp/dpu1/dpu_encoder_phys_cmd.o \
|
||||
disp/dpu1/dpu_encoder_phys_vid.o \
|
||||
disp/dpu1/dpu_encoder_phys_wb.o \
|
||||
disp/dpu1/dpu_formats.o \
|
||||
disp/dpu1/dpu_hw_catalog.o \
|
||||
disp/dpu1/dpu_hw_ctl.o \
|
||||
disp/dpu1/dpu_hw_dsc.o \
|
||||
disp/dpu1/dpu_hw_interrupts.o \
|
||||
disp/dpu1/dpu_hw_intf.o \
|
||||
disp/dpu1/dpu_hw_lm.o \
|
||||
@ -66,11 +75,19 @@ msm-y := \
|
||||
disp/dpu1/dpu_hw_top.o \
|
||||
disp/dpu1/dpu_hw_util.o \
|
||||
disp/dpu1/dpu_hw_vbif.o \
|
||||
disp/dpu1/dpu_hw_wb.o \
|
||||
disp/dpu1/dpu_kms.o \
|
||||
disp/dpu1/dpu_mdss.o \
|
||||
disp/dpu1/dpu_plane.o \
|
||||
disp/dpu1/dpu_rm.o \
|
||||
disp/dpu1/dpu_vbif.o \
|
||||
disp/dpu1/dpu_writeback.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_MDSS) += \
|
||||
msm_mdss.o \
|
||||
|
||||
msm-y += \
|
||||
disp/mdp_format.o \
|
||||
disp/mdp_kms.o \
|
||||
disp/msm_disp_snapshot.o \
|
||||
disp/msm_disp_snapshot_util.o \
|
||||
msm_atomic.o \
|
||||
@ -118,12 +135,10 @@ msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
|
||||
msm-$(CONFIG_DRM_MSM_HDMI_HDCP) += hdmi/hdmi_hdcp.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
|
||||
disp/mdp4/mdp4_dsi_encoder.o \
|
||||
dsi/dsi_cfg.o \
|
||||
dsi/dsi_host.o \
|
||||
dsi/dsi_manager.o \
|
||||
dsi/phy/dsi_phy.o \
|
||||
disp/mdp5/mdp5_cmd_encoder.o
|
||||
dsi/phy/dsi_phy.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/phy/dsi_phy_28nm.o
|
||||
msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o
|
||||
|
@ -1235,7 +1235,7 @@ static void a5xx_fault_detect_irq(struct msm_gpu *gpu)
|
||||
return;
|
||||
|
||||
DRM_DEV_ERROR(dev->dev, "gpu fault ring %d fence %x status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x\n",
|
||||
ring ? ring->id : -1, ring ? ring->seqno : 0,
|
||||
ring ? ring->id : -1, ring ? ring->fctx->last_fence : 0,
|
||||
gpu_read(gpu, REG_A5XX_RBBM_STATUS),
|
||||
gpu_read(gpu, REG_A5XX_CP_RB_RPTR),
|
||||
gpu_read(gpu, REG_A5XX_CP_RB_WPTR),
|
||||
@ -1662,28 +1662,23 @@ static struct msm_ringbuffer *a5xx_active_ring(struct msm_gpu *gpu)
|
||||
return a5xx_gpu->cur_ring;
|
||||
}
|
||||
|
||||
static unsigned long a5xx_gpu_busy(struct msm_gpu *gpu)
|
||||
static u64 a5xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
|
||||
{
|
||||
u64 busy_cycles, busy_time;
|
||||
u64 busy_cycles;
|
||||
|
||||
/* Only read the gpu busy if the hardware is already active */
|
||||
if (pm_runtime_get_if_in_use(&gpu->pdev->dev) == 0)
|
||||
if (pm_runtime_get_if_in_use(&gpu->pdev->dev) == 0) {
|
||||
*out_sample_rate = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
busy_cycles = gpu_read64(gpu, REG_A5XX_RBBM_PERFCTR_RBBM_0_LO,
|
||||
REG_A5XX_RBBM_PERFCTR_RBBM_0_HI);
|
||||
|
||||
busy_time = busy_cycles - gpu->devfreq.busy_cycles;
|
||||
do_div(busy_time, clk_get_rate(gpu->core_clk) / 1000000);
|
||||
|
||||
gpu->devfreq.busy_cycles = busy_cycles;
|
||||
*out_sample_rate = clk_get_rate(gpu->core_clk);
|
||||
|
||||
pm_runtime_put(&gpu->pdev->dev);
|
||||
|
||||
if (WARN_ON(busy_time > ~0LU))
|
||||
return ~0LU;
|
||||
|
||||
return (unsigned long)busy_time;
|
||||
return busy_cycles;
|
||||
}
|
||||
|
||||
static uint32_t a5xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
|
||||
|
@ -1172,7 +1172,7 @@ static int a6xx_gmu_memory_alloc(struct a6xx_gmu *gmu, struct a6xx_gmu_bo *bo,
|
||||
return PTR_ERR(bo->obj);
|
||||
|
||||
ret = msm_gem_get_and_pin_iova_range(bo->obj, gmu->aspace, &bo->iova,
|
||||
range_start >> PAGE_SHIFT, range_end >> PAGE_SHIFT);
|
||||
range_start, range_end);
|
||||
if (ret) {
|
||||
drm_gem_object_put(bo->obj);
|
||||
return ret;
|
||||
|
@ -1390,7 +1390,7 @@ static void a6xx_fault_detect_irq(struct msm_gpu *gpu)
|
||||
|
||||
DRM_DEV_ERROR(&gpu->pdev->dev,
|
||||
"gpu fault ring %d fence %x status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x\n",
|
||||
ring ? ring->id : -1, ring ? ring->seqno : 0,
|
||||
ring ? ring->id : -1, ring ? ring->fctx->last_fence : 0,
|
||||
gpu_read(gpu, REG_A6XX_RBBM_STATUS),
|
||||
gpu_read(gpu, REG_A6XX_CP_RB_RPTR),
|
||||
gpu_read(gpu, REG_A6XX_CP_RB_WPTR),
|
||||
@ -1649,12 +1649,14 @@ static void a6xx_destroy(struct msm_gpu *gpu)
|
||||
kfree(a6xx_gpu);
|
||||
}
|
||||
|
||||
static unsigned long a6xx_gpu_busy(struct msm_gpu *gpu)
|
||||
static u64 a6xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
|
||||
u64 busy_cycles, busy_time;
|
||||
u64 busy_cycles;
|
||||
|
||||
/* 19.2MHz */
|
||||
*out_sample_rate = 19200000;
|
||||
|
||||
/* Only read the gpu busy if the hardware is already active */
|
||||
if (pm_runtime_get_if_in_use(a6xx_gpu->gmu.dev) == 0)
|
||||
@ -1664,17 +1666,10 @@ static unsigned long a6xx_gpu_busy(struct msm_gpu *gpu)
|
||||
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
|
||||
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);
|
||||
|
||||
busy_time = (busy_cycles - gpu->devfreq.busy_cycles) * 10;
|
||||
do_div(busy_time, 192);
|
||||
|
||||
gpu->devfreq.busy_cycles = busy_cycles;
|
||||
|
||||
pm_runtime_put(a6xx_gpu->gmu.dev);
|
||||
|
||||
if (WARN_ON(busy_time > ~0LU))
|
||||
return ~0LU;
|
||||
|
||||
return (unsigned long)busy_time;
|
||||
return busy_cycles;
|
||||
}
|
||||
|
||||
static void a6xx_gpu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp)
|
||||
|
@ -229,10 +229,14 @@ adreno_iommu_create_address_space(struct msm_gpu *gpu,
|
||||
}
|
||||
|
||||
int adreno_get_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
uint32_t param, uint64_t *value)
|
||||
uint32_t param, uint64_t *value, uint32_t *len)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
|
||||
/* No pointer params yet */
|
||||
if (*len != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (param) {
|
||||
case MSM_PARAM_GPU_ID:
|
||||
*value = adreno_gpu->info->revn;
|
||||
@ -272,11 +276,24 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
*value = 0;
|
||||
return 0;
|
||||
case MSM_PARAM_FAULTS:
|
||||
*value = gpu->global_faults + ctx->aspace->faults;
|
||||
if (ctx->aspace)
|
||||
*value = gpu->global_faults + ctx->aspace->faults;
|
||||
else
|
||||
*value = gpu->global_faults;
|
||||
return 0;
|
||||
case MSM_PARAM_SUSPENDS:
|
||||
*value = gpu->suspend_count;
|
||||
return 0;
|
||||
case MSM_PARAM_VA_START:
|
||||
if (ctx->aspace == gpu->aspace)
|
||||
return -EINVAL;
|
||||
*value = ctx->aspace->va_start;
|
||||
return 0;
|
||||
case MSM_PARAM_VA_SIZE:
|
||||
if (ctx->aspace == gpu->aspace)
|
||||
return -EINVAL;
|
||||
*value = ctx->aspace->va_size;
|
||||
return 0;
|
||||
default:
|
||||
DBG("%s: invalid param: %u", gpu->name, param);
|
||||
return -EINVAL;
|
||||
@ -284,9 +301,50 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
}
|
||||
|
||||
int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
uint32_t param, uint64_t value)
|
||||
uint32_t param, uint64_t value, uint32_t len)
|
||||
{
|
||||
switch (param) {
|
||||
case MSM_PARAM_COMM:
|
||||
case MSM_PARAM_CMDLINE:
|
||||
/* kstrdup_quotable_cmdline() limits to PAGE_SIZE, so
|
||||
* that should be a reasonable upper bound
|
||||
*/
|
||||
if (len > PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
if (len != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (param) {
|
||||
case MSM_PARAM_COMM:
|
||||
case MSM_PARAM_CMDLINE: {
|
||||
char *str, **paramp;
|
||||
|
||||
str = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(str, u64_to_user_ptr(value), len)) {
|
||||
kfree(str);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Ensure string is null terminated: */
|
||||
str[len] = '\0';
|
||||
|
||||
if (param == MSM_PARAM_COMM) {
|
||||
paramp = &ctx->comm;
|
||||
} else {
|
||||
paramp = &ctx->cmdline;
|
||||
}
|
||||
|
||||
kfree(*paramp);
|
||||
*paramp = str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case MSM_PARAM_SYSPROF:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
@ -533,7 +591,7 @@ int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state)
|
||||
|
||||
state->ring[i].fence = gpu->rb[i]->memptrs->fence;
|
||||
state->ring[i].iova = gpu->rb[i]->iova;
|
||||
state->ring[i].seqno = gpu->rb[i]->seqno;
|
||||
state->ring[i].seqno = gpu->rb[i]->fctx->last_fence;
|
||||
state->ring[i].rptr = get_rptr(adreno_gpu, gpu->rb[i]);
|
||||
state->ring[i].wptr = get_wptr(gpu->rb[i]);
|
||||
|
||||
@ -783,7 +841,7 @@ void adreno_dump_info(struct msm_gpu *gpu)
|
||||
|
||||
printk("rb %d: fence: %d/%d\n", i,
|
||||
ring->memptrs->fence,
|
||||
ring->seqno);
|
||||
ring->fctx->last_fence);
|
||||
|
||||
printk("rptr: %d\n", get_rptr(adreno_gpu, ring));
|
||||
printk("rb wptr: %d\n", get_wptr(ring));
|
||||
|
@ -281,9 +281,9 @@ static inline int adreno_is_a650_family(struct adreno_gpu *gpu)
|
||||
}
|
||||
|
||||
int adreno_get_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
uint32_t param, uint64_t *value);
|
||||
uint32_t param, uint64_t *value, uint32_t *len);
|
||||
int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
|
||||
uint32_t param, uint64_t value);
|
||||
uint32_t param, uint64_t value, uint32_t len);
|
||||
const struct firmware *adreno_request_fw(struct adreno_gpu *adreno_gpu,
|
||||
const char *fwname);
|
||||
struct drm_gem_object *adreno_fw_create_bo(struct msm_gpu *gpu,
|
||||
|
@ -10,46 +10,42 @@
|
||||
|
||||
/**
|
||||
* dpu_core_irq_preinstall - perform pre-installation of core IRQ handler
|
||||
* @dpu_kms: DPU handle
|
||||
* @kms: MSM KMS handle
|
||||
* @return: none
|
||||
*/
|
||||
void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms);
|
||||
void dpu_core_irq_preinstall(struct msm_kms *kms);
|
||||
|
||||
/**
|
||||
* dpu_core_irq_uninstall - uninstall core IRQ handler
|
||||
* @dpu_kms: DPU handle
|
||||
* @kms: MSM KMS handle
|
||||
* @return: none
|
||||
*/
|
||||
void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms);
|
||||
void dpu_core_irq_uninstall(struct msm_kms *kms);
|
||||
|
||||
/**
|
||||
* dpu_core_irq - core IRQ handler
|
||||
* @dpu_kms: DPU handle
|
||||
* @kms: MSM KMS handle
|
||||
* @return: interrupt handling status
|
||||
*/
|
||||
irqreturn_t dpu_core_irq(struct dpu_kms *dpu_kms);
|
||||
irqreturn_t dpu_core_irq(struct msm_kms *kms);
|
||||
|
||||
/**
|
||||
* dpu_core_irq_read - IRQ helper function for reading IRQ status
|
||||
* @dpu_kms: DPU handle
|
||||
* @irq_idx: irq index
|
||||
* @clear: True to clear the irq after read
|
||||
* @return: non-zero if irq detected; otherwise no irq detected
|
||||
*/
|
||||
u32 dpu_core_irq_read(
|
||||
struct dpu_kms *dpu_kms,
|
||||
int irq_idx,
|
||||
bool clear);
|
||||
int irq_idx);
|
||||
|
||||
/**
|
||||
* dpu_core_irq_register_callback - For registering callback function on IRQ
|
||||
* interrupt
|
||||
* @dpu_kms: DPU handle
|
||||
* @irq_idx: irq index
|
||||
* @irq_cb: IRQ callback structure, containing callback function
|
||||
* and argument. Passing NULL for irq_cb will unregister
|
||||
* the callback for the given irq_idx
|
||||
* This must exist until un-registration.
|
||||
* @irq_cb: IRQ callback funcion.
|
||||
* @irq_arg: IRQ callback argument.
|
||||
* @return: 0 for success registering callback, otherwise failure
|
||||
*
|
||||
* This function supports registration of multiple callbacks for each interrupt.
|
||||
@ -57,25 +53,21 @@ u32 dpu_core_irq_read(
|
||||
int dpu_core_irq_register_callback(
|
||||
struct dpu_kms *dpu_kms,
|
||||
int irq_idx,
|
||||
struct dpu_irq_callback *irq_cb);
|
||||
void (*irq_cb)(void *arg, int irq_idx),
|
||||
void *irq_arg);
|
||||
|
||||
/**
|
||||
* dpu_core_irq_unregister_callback - For unregistering callback function on IRQ
|
||||
* interrupt
|
||||
* @dpu_kms: DPU handle
|
||||
* @irq_idx: irq index
|
||||
* @irq_cb: IRQ callback structure, containing callback function
|
||||
* and argument. Passing NULL for irq_cb will unregister
|
||||
* the callback for the given irq_idx
|
||||
* This must match with registration.
|
||||
* @return: 0 for success registering callback, otherwise failure
|
||||
*
|
||||
* This function supports registration of multiple callbacks for each interrupt.
|
||||
*/
|
||||
int dpu_core_irq_unregister_callback(
|
||||
struct dpu_kms *dpu_kms,
|
||||
int irq_idx,
|
||||
struct dpu_irq_callback *irq_cb);
|
||||
int irq_idx);
|
||||
|
||||
/**
|
||||
* dpu_debugfs_core_irq_init - register core irq debugfs
|
||||
|
@ -204,7 +204,8 @@ static int dpu_crtc_get_crc(struct drm_crtc *crtc)
|
||||
rc = m->hw_lm->ops.collect_misr(m->hw_lm, &crcs[i]);
|
||||
|
||||
if (rc) {
|
||||
DRM_DEBUG_DRIVER("MISR read failed\n");
|
||||
if (rc != -ENODATA)
|
||||
DRM_DEBUG_DRIVER("MISR read failed\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@ -869,6 +870,13 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
|
||||
|
||||
DPU_ATRACE_BEGIN("crtc_commit");
|
||||
|
||||
drm_for_each_encoder_mask(encoder, crtc->dev,
|
||||
crtc->state->encoder_mask) {
|
||||
if (!dpu_encoder_is_valid_for_commit(encoder)) {
|
||||
DRM_DEBUG_ATOMIC("invalid FB not kicking off crtc\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Encoder will flush/start now, unless it has a tx pending. If so, it
|
||||
* may delay and flush at an irq event (e.g. ppdone)
|
||||
@ -891,6 +899,8 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
|
||||
dpu_encoder_kickoff(encoder);
|
||||
|
||||
reinit_completion(&dpu_crtc->frame_done_comp);
|
||||
|
||||
end:
|
||||
DPU_ATRACE_END("crtc_commit");
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
|
||||
@ -21,6 +23,8 @@
|
||||
#include "dpu_hw_intf.h"
|
||||
#include "dpu_hw_ctl.h"
|
||||
#include "dpu_hw_dspp.h"
|
||||
#include "dpu_hw_dsc.h"
|
||||
#include "dpu_hw_merge3d.h"
|
||||
#include "dpu_formats.h"
|
||||
#include "dpu_encoder_phys.h"
|
||||
#include "dpu_crtc.h"
|
||||
@ -34,18 +38,6 @@
|
||||
#define DPU_ERROR_ENC(e, fmt, ...) DPU_ERROR("enc%d " fmt,\
|
||||
(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
|
||||
|
||||
#define DPU_DEBUG_PHYS(p, fmt, ...) DRM_DEBUG_ATOMIC("enc%d intf%d pp%d " fmt,\
|
||||
(p) ? (p)->parent->base.id : -1, \
|
||||
(p) ? (p)->intf_idx - INTF_0 : -1, \
|
||||
(p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define DPU_ERROR_PHYS(p, fmt, ...) DPU_ERROR("enc%d intf%d pp%d " fmt,\
|
||||
(p) ? (p)->parent->base.id : -1, \
|
||||
(p) ? (p)->intf_idx - INTF_0 : -1, \
|
||||
(p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Two to anticipate panels that can do cmd/vid dynamic switching
|
||||
* plan is to create all possible physical encoder types, and switch between
|
||||
@ -135,6 +127,8 @@ enum dpu_enc_rc_states {
|
||||
* @cur_slave: As above but for the slave encoder.
|
||||
* @hw_pp: Handle to the pingpong blocks used for the display. No.
|
||||
* pingpong blocks can be different than num_phys_encs.
|
||||
* @hw_dsc: Handle to the DSC blocks used for the display.
|
||||
* @dsc_mask: Bitmask of used DSC blocks.
|
||||
* @intfs_swapped: Whether or not the phys_enc interfaces have been swapped
|
||||
* for partial update right-only cases, such as pingpong
|
||||
* split where virtual pingpong does not generate IRQs
|
||||
@ -168,6 +162,7 @@ enum dpu_enc_rc_states {
|
||||
* @vsync_event_work: worker to handle vsync event for autorefresh
|
||||
* @topology: topology of the display
|
||||
* @idle_timeout: idle timeout duration in milliseconds
|
||||
* @dsc: msm_display_dsc_config pointer, for DSC-enabled encoders
|
||||
*/
|
||||
struct dpu_encoder_virt {
|
||||
struct drm_encoder base;
|
||||
@ -180,6 +175,9 @@ struct dpu_encoder_virt {
|
||||
struct dpu_encoder_phys *cur_master;
|
||||
struct dpu_encoder_phys *cur_slave;
|
||||
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
|
||||
struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
|
||||
|
||||
unsigned int dsc_mask;
|
||||
|
||||
bool intfs_swapped;
|
||||
|
||||
@ -206,6 +204,11 @@ struct dpu_encoder_virt {
|
||||
struct msm_display_topology topology;
|
||||
|
||||
u32 idle_timeout;
|
||||
|
||||
bool wide_bus_en;
|
||||
|
||||
/* DSC configuration */
|
||||
struct msm_display_dsc_config *dsc;
|
||||
};
|
||||
|
||||
#define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
|
||||
@ -214,6 +217,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = {
|
||||
15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10
|
||||
};
|
||||
|
||||
|
||||
bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
|
||||
{
|
||||
const struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
|
||||
return dpu_enc->wide_bus_en;
|
||||
}
|
||||
|
||||
static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc)
|
||||
{
|
||||
struct dpu_hw_dither_cfg dither_cfg = { 0 };
|
||||
@ -240,12 +251,30 @@ static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bp
|
||||
hw_pp->ops.setup_dither(hw_pp, &dither_cfg);
|
||||
}
|
||||
|
||||
static char *dpu_encoder_helper_get_intf_type(enum dpu_intf_mode intf_mode)
|
||||
{
|
||||
switch (intf_mode) {
|
||||
case INTF_MODE_VIDEO:
|
||||
return "INTF_MODE_VIDEO";
|
||||
case INTF_MODE_CMD:
|
||||
return "INTF_MODE_CMD";
|
||||
case INTF_MODE_WB_BLOCK:
|
||||
return "INTF_MODE_WB_BLOCK";
|
||||
case INTF_MODE_WB_LINE:
|
||||
return "INTF_MODE_WB_LINE";
|
||||
default:
|
||||
return "INTF_MODE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void dpu_encoder_helper_report_irq_timeout(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx)
|
||||
{
|
||||
DRM_ERROR("irq timeout id=%u, intf=%d, pp=%d, intr=%d\n",
|
||||
DRMID(phys_enc->parent), phys_enc->intf_idx - INTF_0,
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, intr_idx);
|
||||
DRM_ERROR("irq timeout id=%u, intf_mode=%s intf=%d wb=%d, pp=%d, intr=%d\n",
|
||||
DRMID(phys_enc->parent),
|
||||
dpu_encoder_helper_get_intf_type(phys_enc->intf_mode),
|
||||
phys_enc->intf_idx - INTF_0, phys_enc->wb_idx - WB_0,
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, intr_idx);
|
||||
|
||||
if (phys_enc->parent_ops->handle_frame_done)
|
||||
phys_enc->parent_ops->handle_frame_done(
|
||||
@ -257,73 +286,69 @@ static int dpu_encoder_helper_wait_event_timeout(int32_t drm_id,
|
||||
u32 irq_idx, struct dpu_encoder_wait_info *info);
|
||||
|
||||
int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx,
|
||||
int irq,
|
||||
void (*func)(void *arg, int irq_idx),
|
||||
struct dpu_encoder_wait_info *wait_info)
|
||||
{
|
||||
struct dpu_encoder_irq *irq;
|
||||
u32 irq_status;
|
||||
int ret;
|
||||
|
||||
if (!wait_info || intr_idx >= INTR_IDX_MAX) {
|
||||
if (!wait_info) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
irq = &phys_enc->irq[intr_idx];
|
||||
|
||||
/* note: do master / slave checking outside */
|
||||
|
||||
/* return EWOULDBLOCK since we know the wait isn't necessary */
|
||||
if (phys_enc->enable_state == DPU_ENC_DISABLED) {
|
||||
DRM_ERROR("encoder is disabled id=%u, intr=%d, irq=%d\n",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx);
|
||||
DRM_ERROR("encoder is disabled id=%u, callback=%ps, irq=%d\n",
|
||||
DRMID(phys_enc->parent), func,
|
||||
irq);
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
if (irq->irq_idx < 0) {
|
||||
DRM_DEBUG_KMS("skip irq wait id=%u, intr=%d, irq=%s\n",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->name);
|
||||
if (irq < 0) {
|
||||
DRM_DEBUG_KMS("skip irq wait id=%u, callback=%ps\n",
|
||||
DRMID(phys_enc->parent), func);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("id=%u, intr=%d, irq=%d, pp=%d, pending_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx, phys_enc->hw_pp->idx - PINGPONG_0,
|
||||
DRM_DEBUG_KMS("id=%u, callback=%ps, irq=%d, pp=%d, pending_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), func,
|
||||
irq, phys_enc->hw_pp->idx - PINGPONG_0,
|
||||
atomic_read(wait_info->atomic_cnt));
|
||||
|
||||
ret = dpu_encoder_helper_wait_event_timeout(
|
||||
DRMID(phys_enc->parent),
|
||||
irq->irq_idx,
|
||||
irq,
|
||||
wait_info);
|
||||
|
||||
if (ret <= 0) {
|
||||
irq_status = dpu_core_irq_read(phys_enc->dpu_kms,
|
||||
irq->irq_idx, true);
|
||||
irq_status = dpu_core_irq_read(phys_enc->dpu_kms, irq);
|
||||
if (irq_status) {
|
||||
unsigned long flags;
|
||||
|
||||
DRM_DEBUG_KMS("irq not triggered id=%u, intr=%d, irq=%d, pp=%d, atomic_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx,
|
||||
DRM_DEBUG_KMS("irq not triggered id=%u, callback=%ps, irq=%d, pp=%d, atomic_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), func,
|
||||
irq,
|
||||
phys_enc->hw_pp->idx - PINGPONG_0,
|
||||
atomic_read(wait_info->atomic_cnt));
|
||||
local_irq_save(flags);
|
||||
irq->cb.func(phys_enc, irq->irq_idx);
|
||||
func(phys_enc, irq);
|
||||
local_irq_restore(flags);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ETIMEDOUT;
|
||||
DRM_DEBUG_KMS("irq timeout id=%u, intr=%d, irq=%d, pp=%d, atomic_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx,
|
||||
DRM_DEBUG_KMS("irq timeout id=%u, callback=%ps, irq=%d, pp=%d, atomic_cnt=%d\n",
|
||||
DRMID(phys_enc->parent), func,
|
||||
irq,
|
||||
phys_enc->hw_pp->idx - PINGPONG_0,
|
||||
atomic_read(wait_info->atomic_cnt));
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
trace_dpu_enc_irq_wait_success(DRMID(phys_enc->parent),
|
||||
intr_idx, irq->irq_idx,
|
||||
func, irq,
|
||||
phys_enc->hw_pp->idx - PINGPONG_0,
|
||||
atomic_read(wait_info->atomic_cnt));
|
||||
}
|
||||
@ -331,70 +356,6 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dpu_encoder_helper_register_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx)
|
||||
{
|
||||
struct dpu_encoder_irq *irq;
|
||||
int ret = 0;
|
||||
|
||||
if (intr_idx >= INTR_IDX_MAX) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
irq = &phys_enc->irq[intr_idx];
|
||||
|
||||
if (irq->irq_idx < 0) {
|
||||
DPU_ERROR_PHYS(phys_enc,
|
||||
"invalid IRQ index:%d\n", irq->irq_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dpu_core_irq_register_callback(phys_enc->dpu_kms, irq->irq_idx,
|
||||
&irq->cb);
|
||||
if (ret) {
|
||||
DPU_ERROR_PHYS(phys_enc,
|
||||
"failed to register IRQ callback for %s\n",
|
||||
irq->name);
|
||||
irq->irq_idx = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
trace_dpu_enc_irq_register_success(DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dpu_encoder_helper_unregister_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx)
|
||||
{
|
||||
struct dpu_encoder_irq *irq;
|
||||
int ret;
|
||||
|
||||
irq = &phys_enc->irq[intr_idx];
|
||||
|
||||
/* silently skip irqs that weren't registered */
|
||||
if (irq->irq_idx < 0) {
|
||||
DRM_ERROR("duplicate unregister id=%u, intr=%d, irq=%d",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = dpu_core_irq_unregister_callback(phys_enc->dpu_kms, irq->irq_idx,
|
||||
&irq->cb);
|
||||
if (ret) {
|
||||
DRM_ERROR("unreg cb fail id=%u, intr=%d, irq=%d ret=%d",
|
||||
DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx, ret);
|
||||
}
|
||||
|
||||
trace_dpu_enc_irq_unregister_success(DRMID(phys_enc->parent), intr_idx,
|
||||
irq->irq_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
@ -501,6 +462,22 @@ void dpu_encoder_helper_split_config(
|
||||
}
|
||||
}
|
||||
|
||||
bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
int i, intf_count = 0, num_dsc = 0;
|
||||
|
||||
for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
|
||||
if (dpu_enc->phys_encs[i])
|
||||
intf_count++;
|
||||
|
||||
/* See dpu_encoder_get_topology, we only support 2:2:1 topology */
|
||||
if (dpu_enc->dsc)
|
||||
num_dsc = 2;
|
||||
|
||||
return (num_dsc > 0) && (num_dsc > intf_count);
|
||||
}
|
||||
|
||||
static struct msm_display_topology dpu_encoder_get_topology(
|
||||
struct dpu_encoder_virt *dpu_enc,
|
||||
struct dpu_kms *dpu_kms,
|
||||
@ -541,8 +518,21 @@ static struct msm_display_topology dpu_encoder_get_topology(
|
||||
topology.num_enc = 0;
|
||||
topology.num_intf = intf_count;
|
||||
|
||||
if (dpu_enc->dsc) {
|
||||
/* In case of Display Stream Compression (DSC), we would use
|
||||
* 2 encoders, 2 layer mixers and 1 interface
|
||||
* this is power optimal and can drive up to (including) 4k
|
||||
* screens
|
||||
*/
|
||||
topology.num_enc = 2;
|
||||
topology.num_dsc = 2;
|
||||
topology.num_intf = 1;
|
||||
topology.num_lm = 2;
|
||||
}
|
||||
|
||||
return topology;
|
||||
}
|
||||
|
||||
static int dpu_encoder_virt_atomic_check(
|
||||
struct drm_encoder *drm_enc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
@ -929,6 +919,40 @@ static int dpu_encoder_resource_control(struct drm_encoder *drm_enc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
int i;
|
||||
|
||||
dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
|
||||
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
|
||||
struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
|
||||
|
||||
if (phys->ops.prepare_wb_job)
|
||||
phys->ops.prepare_wb_job(phys, job);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
int i;
|
||||
|
||||
dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
|
||||
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
|
||||
struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
|
||||
|
||||
if (phys->ops.cleanup_wb_job)
|
||||
phys->ops.cleanup_wb_job(phys, job);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
@ -942,7 +966,9 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
|
||||
struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC];
|
||||
struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC];
|
||||
struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_ENC] = { NULL };
|
||||
int num_lm, num_ctl, num_pp;
|
||||
struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
|
||||
int num_lm, num_ctl, num_pp, num_dsc;
|
||||
unsigned int dsc_mask = 0;
|
||||
int i;
|
||||
|
||||
if (!drm_enc) {
|
||||
@ -980,6 +1006,18 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
|
||||
dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
|
||||
: NULL;
|
||||
|
||||
if (dpu_enc->dsc) {
|
||||
num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
|
||||
drm_enc->base.id, DPU_HW_BLK_DSC,
|
||||
hw_dsc, ARRAY_SIZE(hw_dsc));
|
||||
for (i = 0; i < num_dsc; i++) {
|
||||
dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
|
||||
dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
|
||||
}
|
||||
}
|
||||
|
||||
dpu_enc->dsc_mask = dsc_mask;
|
||||
|
||||
cstate = to_dpu_crtc_state(crtc_state);
|
||||
|
||||
for (i = 0; i < num_lm; i++) {
|
||||
@ -1015,9 +1053,18 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
|
||||
if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
|
||||
phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
|
||||
|
||||
if (!phys->hw_intf) {
|
||||
if (phys->wb_idx >= WB_0 && phys->wb_idx < WB_MAX)
|
||||
phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm, phys->wb_idx);
|
||||
|
||||
if (!phys->hw_intf && !phys->hw_wb) {
|
||||
DPU_ERROR_ENC(dpu_enc,
|
||||
"no intf block assigned at idx: %d\n", i);
|
||||
"no intf or wb block assigned at idx: %d\n", i);
|
||||
return;
|
||||
}
|
||||
|
||||
if (phys->hw_intf && phys->hw_wb) {
|
||||
DPU_ERROR_ENC(dpu_enc,
|
||||
"invalid phys both intf and wb block at idx: %d\n", i);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1165,16 +1212,35 @@ static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < catalog->intf_count; i++) {
|
||||
if (catalog->intf[i].type == type
|
||||
&& catalog->intf[i].controller_id == controller_id) {
|
||||
return catalog->intf[i].id;
|
||||
if (type != INTF_WB) {
|
||||
for (i = 0; i < catalog->intf_count; i++) {
|
||||
if (catalog->intf[i].type == type
|
||||
&& catalog->intf[i].controller_id == controller_id) {
|
||||
return catalog->intf[i].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INTF_MAX;
|
||||
}
|
||||
|
||||
static enum dpu_wb dpu_encoder_get_wb(struct dpu_mdss_cfg *catalog,
|
||||
enum dpu_intf_type type, u32 controller_id)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (type != INTF_WB)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < catalog->wb_count; i++) {
|
||||
if (catalog->wb[i].id == controller_id)
|
||||
return catalog->wb[i].id;
|
||||
}
|
||||
|
||||
end:
|
||||
return WB_MAX;
|
||||
}
|
||||
|
||||
static void dpu_encoder_vblank_callback(struct drm_encoder *drm_enc,
|
||||
struct dpu_encoder_phys *phy_enc)
|
||||
{
|
||||
@ -1288,8 +1354,9 @@ static void dpu_encoder_frame_done_callback(
|
||||
* suppress frame_done without waiter,
|
||||
* likely autorefresh
|
||||
*/
|
||||
trace_dpu_enc_frame_done_cb_not_busy(DRMID(drm_enc),
|
||||
event, ready_phys->intf_idx);
|
||||
trace_dpu_enc_frame_done_cb_not_busy(DRMID(drm_enc), event,
|
||||
dpu_encoder_helper_get_intf_type(ready_phys->intf_mode),
|
||||
ready_phys->intf_idx, ready_phys->wb_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1367,9 +1434,11 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc,
|
||||
if (ctl->ops.get_pending_flush)
|
||||
ret = ctl->ops.get_pending_flush(ctl);
|
||||
|
||||
trace_dpu_enc_trigger_flush(DRMID(drm_enc), phys->intf_idx,
|
||||
pending_kickoff_cnt, ctl->idx,
|
||||
extra_flush_bits, ret);
|
||||
trace_dpu_enc_trigger_flush(DRMID(drm_enc),
|
||||
dpu_encoder_helper_get_intf_type(phys->intf_mode),
|
||||
phys->intf_idx, phys->wb_idx,
|
||||
pending_kickoff_cnt, ctl->idx,
|
||||
extra_flush_bits, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1677,6 +1746,95 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
|
||||
nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
|
||||
}
|
||||
|
||||
static u32
|
||||
dpu_encoder_dsc_initial_line_calc(struct msm_display_dsc_config *dsc,
|
||||
u32 enc_ip_width)
|
||||
{
|
||||
int ssm_delay, total_pixels, soft_slice_per_enc;
|
||||
|
||||
soft_slice_per_enc = enc_ip_width / dsc->drm->slice_width;
|
||||
|
||||
/*
|
||||
* minimum number of initial line pixels is a sum of:
|
||||
* 1. sub-stream multiplexer delay (83 groups for 8bpc,
|
||||
* 91 for 10 bpc) * 3
|
||||
* 2. for two soft slice cases, add extra sub-stream multiplexer * 3
|
||||
* 3. the initial xmit delay
|
||||
* 4. total pipeline delay through the "lock step" of encoder (47)
|
||||
* 5. 6 additional pixels as the output of the rate buffer is
|
||||
* 48 bits wide
|
||||
*/
|
||||
ssm_delay = ((dsc->drm->bits_per_component < 10) ? 84 : 92);
|
||||
total_pixels = ssm_delay * 3 + dsc->drm->initial_xmit_delay + 47;
|
||||
if (soft_slice_per_enc > 1)
|
||||
total_pixels += (ssm_delay * 3);
|
||||
return DIV_ROUND_UP(total_pixels, dsc->drm->slice_width);
|
||||
}
|
||||
|
||||
static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_dsc *hw_dsc,
|
||||
struct dpu_hw_pingpong *hw_pp,
|
||||
struct msm_display_dsc_config *dsc,
|
||||
u32 common_mode,
|
||||
u32 initial_lines)
|
||||
{
|
||||
if (hw_dsc->ops.dsc_config)
|
||||
hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, initial_lines);
|
||||
|
||||
if (hw_dsc->ops.dsc_config_thresh)
|
||||
hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);
|
||||
|
||||
if (hw_pp->ops.setup_dsc)
|
||||
hw_pp->ops.setup_dsc(hw_pp);
|
||||
|
||||
if (hw_pp->ops.enable_dsc)
|
||||
hw_pp->ops.enable_dsc(hw_pp);
|
||||
}
|
||||
|
||||
static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
|
||||
struct msm_display_dsc_config *dsc)
|
||||
{
|
||||
/* coding only for 2LM, 2enc, 1 dsc config */
|
||||
struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
|
||||
struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
|
||||
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
|
||||
int this_frame_slices;
|
||||
int intf_ip_w, enc_ip_w;
|
||||
int dsc_common_mode;
|
||||
int pic_width;
|
||||
u32 initial_lines;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
|
||||
hw_pp[i] = dpu_enc->hw_pp[i];
|
||||
hw_dsc[i] = dpu_enc->hw_dsc[i];
|
||||
|
||||
if (!hw_pp[i] || !hw_dsc[i]) {
|
||||
DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dsc_common_mode = 0;
|
||||
pic_width = dsc->drm->pic_width;
|
||||
|
||||
dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
|
||||
if (enc_master->intf_mode == INTF_MODE_VIDEO)
|
||||
dsc_common_mode |= DSC_MODE_VIDEO;
|
||||
|
||||
this_frame_slices = pic_width / dsc->drm->slice_width;
|
||||
intf_ip_w = this_frame_slices * dsc->drm->slice_width;
|
||||
|
||||
/*
|
||||
* dsc merge case: when using 2 encoders for the same stream,
|
||||
* no. of slices need to be same on both the encoders.
|
||||
*/
|
||||
enc_ip_w = intf_ip_w / 2;
|
||||
initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
|
||||
|
||||
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
|
||||
dpu_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], dsc, dsc_common_mode, initial_lines);
|
||||
}
|
||||
|
||||
void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
@ -1708,6 +1866,30 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
|
||||
dpu_encoder_helper_hw_reset(dpu_enc->phys_encs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (dpu_enc->dsc)
|
||||
dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
|
||||
}
|
||||
|
||||
bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
unsigned int i;
|
||||
struct dpu_encoder_phys *phys;
|
||||
|
||||
dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
|
||||
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
|
||||
phys = dpu_enc->phys_encs[i];
|
||||
if (phys->ops.is_valid_for_commit && !phys->ops.is_valid_for_commit(phys)) {
|
||||
DPU_DEBUG("invalid FB not kicking off\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
|
||||
@ -1751,6 +1933,102 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
|
||||
DPU_ATRACE_END("encoder_kickoff");
|
||||
}
|
||||
|
||||
static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_mixer_cfg mixer;
|
||||
int i, num_lm;
|
||||
u32 flush_mask = 0;
|
||||
struct dpu_global_state *global_state;
|
||||
struct dpu_hw_blk *hw_lm[2];
|
||||
struct dpu_hw_mixer *hw_mixer[2];
|
||||
struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
|
||||
|
||||
memset(&mixer, 0, sizeof(mixer));
|
||||
|
||||
/* reset all mixers for this encoder */
|
||||
if (phys_enc->hw_ctl->ops.clear_all_blendstages)
|
||||
phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
|
||||
|
||||
global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
|
||||
|
||||
num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state,
|
||||
phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm));
|
||||
|
||||
for (i = 0; i < num_lm; i++) {
|
||||
hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
|
||||
flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, hw_mixer[i]->idx);
|
||||
if (phys_enc->hw_ctl->ops.update_pending_flush)
|
||||
phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask);
|
||||
|
||||
/* clear all blendstages */
|
||||
if (phys_enc->hw_ctl->ops.setup_blendstage)
|
||||
phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
|
||||
struct dpu_hw_intf_cfg intf_cfg = { 0 };
|
||||
int i;
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
|
||||
dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
|
||||
|
||||
phys_enc->hw_ctl->ops.reset(ctl);
|
||||
|
||||
dpu_encoder_helper_reset_mixers(phys_enc);
|
||||
|
||||
/*
|
||||
* TODO: move the once-only operation like CTL flush/trigger
|
||||
* into dpu_encoder_virt_disable() and all operations which need
|
||||
* to be done per phys encoder into the phys_disable() op.
|
||||
*/
|
||||
if (phys_enc->hw_wb) {
|
||||
/* disable the PP block */
|
||||
if (phys_enc->hw_wb->ops.bind_pingpong_blk)
|
||||
phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, false,
|
||||
phys_enc->hw_pp->idx);
|
||||
|
||||
/* mark WB flush as pending */
|
||||
if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
|
||||
phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx);
|
||||
} else {
|
||||
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
|
||||
if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
|
||||
phys_enc->hw_intf->ops.bind_pingpong_blk(
|
||||
dpu_enc->phys_encs[i]->hw_intf, false,
|
||||
dpu_enc->phys_encs[i]->hw_pp->idx);
|
||||
|
||||
/* mark INTF flush as pending */
|
||||
if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
|
||||
phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
|
||||
dpu_enc->phys_encs[i]->hw_intf->idx);
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the merge 3D HW block */
|
||||
if (phys_enc->hw_pp->merge_3d) {
|
||||
phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
|
||||
BLEND_3D_NONE);
|
||||
if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d)
|
||||
phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl,
|
||||
phys_enc->hw_pp->merge_3d->idx);
|
||||
}
|
||||
|
||||
intf_cfg.stream_sel = 0; /* Don't care value for video mode */
|
||||
intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
|
||||
if (phys_enc->hw_pp->merge_3d)
|
||||
intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
|
||||
|
||||
if (ctl->ops.reset_intf_cfg)
|
||||
ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
|
||||
|
||||
ctl->ops.trigger_flush(ctl);
|
||||
ctl->ops.trigger_start(ctl);
|
||||
ctl->ops.clear_pending_flush(ctl);
|
||||
}
|
||||
|
||||
void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
@ -1780,22 +2058,12 @@ static int _dpu_encoder_status_show(struct seq_file *s, void *data)
|
||||
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
|
||||
struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
|
||||
|
||||
seq_printf(s, "intf:%d vsync:%8d underrun:%8d ",
|
||||
phys->intf_idx - INTF_0,
|
||||
seq_printf(s, "intf:%d wb:%d vsync:%8d underrun:%8d ",
|
||||
phys->intf_idx - INTF_0, phys->wb_idx - WB_0,
|
||||
atomic_read(&phys->vsync_cnt),
|
||||
atomic_read(&phys->underrun_cnt));
|
||||
|
||||
switch (phys->intf_mode) {
|
||||
case INTF_MODE_VIDEO:
|
||||
seq_puts(s, "mode: video\n");
|
||||
break;
|
||||
case INTF_MODE_CMD:
|
||||
seq_puts(s, "mode: command\n");
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "mode: ???\n");
|
||||
break;
|
||||
}
|
||||
seq_printf(s, "mode: %s\n", dpu_encoder_helper_get_intf_type(phys->intf_mode));
|
||||
}
|
||||
mutex_unlock(&dpu_enc->enc_lock);
|
||||
|
||||
@ -1854,7 +2122,7 @@ static void dpu_encoder_early_unregister(struct drm_encoder *encoder)
|
||||
}
|
||||
|
||||
static int dpu_encoder_virt_add_phys_encs(
|
||||
u32 display_caps,
|
||||
struct msm_display_info *disp_info,
|
||||
struct dpu_encoder_virt *dpu_enc,
|
||||
struct dpu_enc_phys_init_params *params)
|
||||
{
|
||||
@ -1873,7 +2141,7 @@ static int dpu_encoder_virt_add_phys_encs(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (display_caps & MSM_DISPLAY_CAP_VID_MODE) {
|
||||
if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) {
|
||||
enc = dpu_encoder_phys_vid_init(params);
|
||||
|
||||
if (IS_ERR_OR_NULL(enc)) {
|
||||
@ -1886,7 +2154,7 @@ static int dpu_encoder_virt_add_phys_encs(
|
||||
++dpu_enc->num_phys_encs;
|
||||
}
|
||||
|
||||
if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) {
|
||||
if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
|
||||
enc = dpu_encoder_phys_cmd_init(params);
|
||||
|
||||
if (IS_ERR_OR_NULL(enc)) {
|
||||
@ -1899,6 +2167,19 @@ static int dpu_encoder_virt_add_phys_encs(
|
||||
++dpu_enc->num_phys_encs;
|
||||
}
|
||||
|
||||
if (disp_info->intf_type == DRM_MODE_ENCODER_VIRTUAL) {
|
||||
enc = dpu_encoder_phys_wb_init(params);
|
||||
|
||||
if (IS_ERR_OR_NULL(enc)) {
|
||||
DPU_ERROR_ENC(dpu_enc, "failed to init wb enc: %ld\n",
|
||||
PTR_ERR(enc));
|
||||
return enc == NULL ? -EINVAL : PTR_ERR(enc);
|
||||
}
|
||||
|
||||
dpu_enc->phys_encs[dpu_enc->num_phys_encs] = enc;
|
||||
++dpu_enc->num_phys_encs;
|
||||
}
|
||||
|
||||
if (params->split_role == ENC_ROLE_SLAVE)
|
||||
dpu_enc->cur_slave = enc;
|
||||
else
|
||||
@ -1942,6 +2223,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
|
||||
case DRM_MODE_ENCODER_TMDS:
|
||||
intf_type = INTF_DP;
|
||||
break;
|
||||
case DRM_MODE_ENCODER_VIRTUAL:
|
||||
intf_type = INTF_WB;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON(disp_info->num_of_h_tiles < 1);
|
||||
@ -1953,6 +2237,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
|
||||
dpu_enc->idle_pc_supported =
|
||||
dpu_kms->catalog->caps->has_idle_pc;
|
||||
|
||||
dpu_enc->dsc = disp_info->dsc;
|
||||
|
||||
mutex_lock(&dpu_enc->enc_lock);
|
||||
for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
|
||||
/*
|
||||
@ -1977,16 +2263,30 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
|
||||
phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
|
||||
intf_type,
|
||||
controller_id);
|
||||
if (phys_params.intf_idx == INTF_MAX) {
|
||||
DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id %d\n",
|
||||
|
||||
phys_params.wb_idx = dpu_encoder_get_wb(dpu_kms->catalog,
|
||||
intf_type, controller_id);
|
||||
/*
|
||||
* The phys_params might represent either an INTF or a WB unit, but not
|
||||
* both of them at the same time.
|
||||
*/
|
||||
if ((phys_params.intf_idx == INTF_MAX) &&
|
||||
(phys_params.wb_idx == WB_MAX)) {
|
||||
DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d, id %d\n",
|
||||
intf_type, controller_id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if ((phys_params.intf_idx != INTF_MAX) &&
|
||||
(phys_params.wb_idx != WB_MAX)) {
|
||||
DPU_ERROR_ENC(dpu_enc, "both intf and wb present: type %d, id %d\n",
|
||||
intf_type, controller_id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
ret = dpu_encoder_virt_add_phys_encs(disp_info->capabilities,
|
||||
dpu_enc,
|
||||
&phys_params);
|
||||
ret = dpu_encoder_virt_add_phys_encs(disp_info,
|
||||
dpu_enc, &phys_params);
|
||||
if (ret)
|
||||
DPU_ERROR_ENC(dpu_enc, "failed to add phys encs\n");
|
||||
}
|
||||
@ -2066,6 +2366,9 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc,
|
||||
timer_setup(&dpu_enc->vsync_event_timer,
|
||||
dpu_encoder_vsync_event_handler,
|
||||
0);
|
||||
else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS)
|
||||
dpu_enc->wide_bus_en = msm_dp_wide_bus_available(
|
||||
priv->dp[disp_info->h_tile_instance[0]]);
|
||||
|
||||
INIT_DELAYED_WORK(&dpu_enc->delayed_off_work,
|
||||
dpu_encoder_off_work);
|
||||
@ -2100,8 +2403,9 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev,
|
||||
if (!dpu_enc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
||||
rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs,
|
||||
drm_enc_mode, NULL);
|
||||
drm_enc_mode, NULL);
|
||||
if (rc) {
|
||||
devm_kfree(dev->dev, dpu_enc);
|
||||
return ERR_PTR(rc);
|
||||
@ -2180,3 +2484,11 @@ enum dpu_intf_mode dpu_encoder_get_intf_mode(struct drm_encoder *encoder)
|
||||
|
||||
return INTF_MODE_NONE;
|
||||
}
|
||||
|
||||
unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct drm_encoder *encoder = phys_enc->parent;
|
||||
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(encoder);
|
||||
|
||||
return dpu_enc->dsc_mask;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
* based on num_of_h_tiles
|
||||
* @is_te_using_watchdog_timer: Boolean to indicate watchdog TE is
|
||||
* used instead of panel TE in cmd mode panels
|
||||
* @dsc: DSC configuration data for DSC-enabled displays
|
||||
*/
|
||||
struct msm_display_info {
|
||||
int intf_type;
|
||||
@ -34,6 +35,7 @@ struct msm_display_info {
|
||||
uint32_t num_of_h_tiles;
|
||||
uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
|
||||
bool is_te_using_watchdog_timer;
|
||||
struct msm_display_dsc_config *dsc;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -170,4 +172,34 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc);
|
||||
*/
|
||||
int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc);
|
||||
|
||||
bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc);
|
||||
|
||||
/**
|
||||
* dpu_encoder_use_dsc_merge - returns true if the encoder uses DSC merge topology.
|
||||
* @drm_enc: Pointer to previously created drm encoder structure
|
||||
*/
|
||||
bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc);
|
||||
|
||||
/**
|
||||
* dpu_encoder_prepare_wb_job - prepare writeback job for the encoder.
|
||||
* @drm_enc: Pointer to previously created drm encoder structure
|
||||
* @job: Pointer to the current drm writeback job
|
||||
*/
|
||||
void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
|
||||
struct drm_writeback_job *job);
|
||||
|
||||
/**
|
||||
* dpu_encoder_cleanup_wb_job - cleanup writeback job for the encoder.
|
||||
* @drm_enc: Pointer to previously created drm encoder structure
|
||||
* @job: Pointer to the current drm writeback job
|
||||
*/
|
||||
void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
|
||||
struct drm_writeback_job *job);
|
||||
|
||||
/**
|
||||
* dpu_encoder_is_valid_for_commit - check if encode has valid parameters for commit.
|
||||
* @drm_enc: Pointer to drm encoder structure
|
||||
*/
|
||||
bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc);
|
||||
|
||||
#endif /* __DPU_ENCODER_H__ */
|
||||
|
@ -1,15 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DPU_ENCODER_PHYS_H__
|
||||
#define __DPU_ENCODER_PHYS_H__
|
||||
|
||||
#include <drm/drm_writeback.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include "dpu_kms.h"
|
||||
#include "dpu_hw_intf.h"
|
||||
#include "dpu_hw_wb.h"
|
||||
#include "dpu_hw_pingpong.h"
|
||||
#include "dpu_hw_ctl.h"
|
||||
#include "dpu_hw_top.h"
|
||||
@ -135,6 +138,11 @@ struct dpu_encoder_phys_ops {
|
||||
void (*restore)(struct dpu_encoder_phys *phys);
|
||||
int (*get_line_count)(struct dpu_encoder_phys *phys);
|
||||
int (*get_frame_count)(struct dpu_encoder_phys *phys);
|
||||
void (*prepare_wb_job)(struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_writeback_job *job);
|
||||
void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_writeback_job *job);
|
||||
bool (*is_valid_for_commit)(struct dpu_encoder_phys *phys_enc);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -143,6 +151,7 @@ struct dpu_encoder_phys_ops {
|
||||
* @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
|
||||
* @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
|
||||
* @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel
|
||||
* @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector
|
||||
*/
|
||||
enum dpu_intr_idx {
|
||||
INTR_IDX_VSYNC,
|
||||
@ -150,24 +159,10 @@ enum dpu_intr_idx {
|
||||
INTR_IDX_UNDERRUN,
|
||||
INTR_IDX_CTL_START,
|
||||
INTR_IDX_RDPTR,
|
||||
INTR_IDX_WB_DONE,
|
||||
INTR_IDX_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* dpu_encoder_irq - tracking structure for interrupts
|
||||
* @name: string name of interrupt
|
||||
* @intr_idx: Encoder interrupt enumeration
|
||||
* @irq_idx: IRQ interface lookup index from DPU IRQ framework
|
||||
* will be -EINVAL if IRQ is not registered
|
||||
* @irq_cb: interrupt callback
|
||||
*/
|
||||
struct dpu_encoder_irq {
|
||||
const char *name;
|
||||
enum dpu_intr_idx intr_idx;
|
||||
int irq_idx;
|
||||
struct dpu_irq_callback cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_encoder_phys - physical encoder that drives a single INTF block
|
||||
* tied to a specific panel / sub-panel. Abstract type, sub-classed by
|
||||
@ -179,12 +174,14 @@ struct dpu_encoder_irq {
|
||||
* @hw_ctl: Hardware interface to the ctl registers
|
||||
* @hw_pp: Hardware interface to the ping pong registers
|
||||
* @hw_intf: Hardware interface to the intf registers
|
||||
* @hw_wb: Hardware interface to the wb registers
|
||||
* @dpu_kms: Pointer to the dpu_kms top level
|
||||
* @cached_mode: DRM mode cached at mode_set time, acted on in enable
|
||||
* @enabled: Whether the encoder has enabled and running a mode
|
||||
* @split_role: Role to play in a split-panel configuration
|
||||
* @intf_mode: Interface mode
|
||||
* @intf_idx: Interface index on dpu hardware
|
||||
* @wb_idx: Writeback index on dpu hardware
|
||||
* @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes
|
||||
* @enable_state: Enable state tracking
|
||||
* @vblank_refcount: Reference count of vblank request
|
||||
@ -197,7 +194,7 @@ struct dpu_encoder_irq {
|
||||
* @pending_ctlstart_cnt: Atomic counter tracking the number of ctl start
|
||||
* pending.
|
||||
* @pending_kickoff_wq: Wait queue for blocking until kickoff completes
|
||||
* @irq: IRQ tracking structures
|
||||
* @irq: IRQ indices
|
||||
*/
|
||||
struct dpu_encoder_phys {
|
||||
struct drm_encoder *parent;
|
||||
@ -207,11 +204,13 @@ struct dpu_encoder_phys {
|
||||
struct dpu_hw_ctl *hw_ctl;
|
||||
struct dpu_hw_pingpong *hw_pp;
|
||||
struct dpu_hw_intf *hw_intf;
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_kms *dpu_kms;
|
||||
struct drm_display_mode cached_mode;
|
||||
enum dpu_enc_split_role split_role;
|
||||
enum dpu_intf_mode intf_mode;
|
||||
enum dpu_intf intf_idx;
|
||||
enum dpu_wb wb_idx;
|
||||
spinlock_t *enc_spinlock;
|
||||
enum dpu_enc_enable_state enable_state;
|
||||
atomic_t vblank_refcount;
|
||||
@ -220,7 +219,7 @@ struct dpu_encoder_phys {
|
||||
atomic_t pending_ctlstart_cnt;
|
||||
atomic_t pending_kickoff_cnt;
|
||||
wait_queue_head_t pending_kickoff_wq;
|
||||
struct dpu_encoder_irq irq[INTR_IDX_MAX];
|
||||
int irq[INTR_IDX_MAX];
|
||||
};
|
||||
|
||||
static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
|
||||
@ -229,6 +228,27 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
|
||||
return atomic_inc_return(&phys->pending_kickoff_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to handle command
|
||||
* mode specific operations
|
||||
* @base: Baseclass physical encoder structure
|
||||
* @wbirq_refcount: Reference count of writeback interrupt
|
||||
* @wb_done_timeout_cnt: number of wb done irq timeout errors
|
||||
* @wb_cfg: writeback block config to store fb related details
|
||||
* @wb_conn: backpointer to writeback connector
|
||||
* @wb_job: backpointer to current writeback job
|
||||
* @dest: dpu buffer layout for current writeback output buffer
|
||||
*/
|
||||
struct dpu_encoder_phys_wb {
|
||||
struct dpu_encoder_phys base;
|
||||
atomic_t wbirq_refcount;
|
||||
int wb_done_timeout_cnt;
|
||||
struct dpu_hw_wb_cfg wb_cfg;
|
||||
struct drm_writeback_connector *wb_conn;
|
||||
struct drm_writeback_job *wb_job;
|
||||
struct dpu_hw_fmt_layout dest;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command
|
||||
* mode specific operations
|
||||
@ -257,6 +277,7 @@ struct dpu_encoder_phys_cmd {
|
||||
* @parent_ops: Callbacks exposed by the parent to the phys_enc
|
||||
* @split_role: Role to play in a split-panel configuration
|
||||
* @intf_idx: Interface index this phys_enc will control
|
||||
* @wb_idx: Writeback index this phys_enc will control
|
||||
* @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes
|
||||
*/
|
||||
struct dpu_enc_phys_init_params {
|
||||
@ -265,6 +286,7 @@ struct dpu_enc_phys_init_params {
|
||||
const struct dpu_encoder_virt_ops *parent_ops;
|
||||
enum dpu_enc_split_role split_role;
|
||||
enum dpu_intf intf_idx;
|
||||
enum dpu_wb wb_idx;
|
||||
spinlock_t *enc_spinlock;
|
||||
};
|
||||
|
||||
@ -296,6 +318,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
|
||||
struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
|
||||
struct dpu_enc_phys_init_params *p);
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_init - initialize writeback encoder
|
||||
* @init: Pointer to init info structure with initialization params
|
||||
*/
|
||||
struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
|
||||
struct dpu_enc_phys_init_params *p);
|
||||
|
||||
/**
|
||||
* dpu_encoder_helper_trigger_start - control start helper function
|
||||
* This helper function may be optionally specified by physical
|
||||
@ -314,13 +343,23 @@ static inline enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(
|
||||
|
||||
dpu_cstate = to_dpu_crtc_state(phys_enc->parent->crtc->state);
|
||||
|
||||
/* Use merge_3d unless DSC MERGE topology is used */
|
||||
if (phys_enc->split_role == ENC_ROLE_SOLO &&
|
||||
dpu_cstate->num_mixers == CRTC_DUAL_MIXERS)
|
||||
dpu_cstate->num_mixers == CRTC_DUAL_MIXERS &&
|
||||
!dpu_encoder_use_dsc_merge(phys_enc->parent))
|
||||
return BLEND_3D_H_ROW_INT;
|
||||
|
||||
return BLEND_3D_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_helper_get_dsc - get DSC blocks mask for the DPU encoder
|
||||
* This helper function is used by physical encoder to get DSC blocks mask
|
||||
* used for this encoder.
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
*/
|
||||
unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc);
|
||||
|
||||
/**
|
||||
* dpu_encoder_helper_split_config - split display configuration helper function
|
||||
* This helper function may be used by physical encoders to configure
|
||||
@ -345,30 +384,20 @@ void dpu_encoder_helper_report_irq_timeout(struct dpu_encoder_phys *phys_enc,
|
||||
* dpu_encoder_helper_wait_for_irq - utility to wait on an irq.
|
||||
* note: will call dpu_encoder_helper_wait_for_irq on timeout
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @intr_idx: encoder interrupt index
|
||||
* @irq: IRQ index
|
||||
* @func: IRQ callback to be called in case of timeout
|
||||
* @wait_info: wait info struct
|
||||
* @Return: 0 or -ERROR
|
||||
*/
|
||||
int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx,
|
||||
int irq,
|
||||
void (*func)(void *arg, int irq_idx),
|
||||
struct dpu_encoder_wait_info *wait_info);
|
||||
|
||||
/**
|
||||
* dpu_encoder_helper_register_irq - register and enable an irq
|
||||
* dpu_encoder_helper_phys_cleanup - helper to cleanup dpu pipeline
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @intr_idx: encoder interrupt index
|
||||
* @Return: 0 or -ERROR
|
||||
*/
|
||||
int dpu_encoder_helper_register_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx);
|
||||
|
||||
/**
|
||||
* dpu_encoder_helper_unregister_irq - unregister and disable an irq
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @intr_idx: encoder interrupt index
|
||||
* @Return: 0 or -ERROR
|
||||
*/
|
||||
int dpu_encoder_helper_unregister_irq(struct dpu_encoder_phys *phys_enc,
|
||||
enum dpu_intr_idx intr_idx);
|
||||
void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc);
|
||||
|
||||
#endif /* __dpu_encoder_phys_H__ */
|
||||
|
@ -62,6 +62,13 @@ static void _dpu_encoder_phys_cmd_update_intf_cfg(
|
||||
intf_cfg.stream_sel = cmd_enc->stream_sel;
|
||||
intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
|
||||
ctl->ops.setup_intf_cfg(ctl, &intf_cfg);
|
||||
|
||||
/* setup which pp blk will connect to this intf */
|
||||
if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) && phys_enc->hw_intf->ops.bind_pingpong_blk)
|
||||
phys_enc->hw_intf->ops.bind_pingpong_blk(
|
||||
phys_enc->hw_intf,
|
||||
true,
|
||||
phys_enc->hw_pp->idx);
|
||||
}
|
||||
|
||||
static void dpu_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
|
||||
@ -140,19 +147,13 @@ static void dpu_encoder_phys_cmd_atomic_mode_set(
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct dpu_encoder_irq *irq;
|
||||
phys_enc->irq[INTR_IDX_CTL_START] = phys_enc->hw_ctl->caps->intr_start;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_CTL_START];
|
||||
irq->irq_idx = phys_enc->hw_ctl->caps->intr_start;
|
||||
phys_enc->irq[INTR_IDX_PINGPONG] = phys_enc->hw_pp->caps->intr_done;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_PINGPONG];
|
||||
irq->irq_idx = phys_enc->hw_pp->caps->intr_done;
|
||||
phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_pp->caps->intr_rdptr;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_RDPTR];
|
||||
irq->irq_idx = phys_enc->hw_pp->caps->intr_rdptr;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
|
||||
irq->irq_idx = phys_enc->hw_intf->cap->intr_underrun;
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN] = phys_enc->hw_intf->cap->intr_underrun;
|
||||
}
|
||||
|
||||
static int _dpu_encoder_phys_cmd_handle_ppdone_timeout(
|
||||
@ -192,7 +193,8 @@ static int _dpu_encoder_phys_cmd_handle_ppdone_timeout(
|
||||
cmd_enc->pp_timeout_report_cnt,
|
||||
atomic_read(&phys_enc->pending_kickoff_cnt));
|
||||
msm_disp_snapshot_state(drm_enc->dev);
|
||||
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_RDPTR]);
|
||||
}
|
||||
|
||||
atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
|
||||
@ -219,7 +221,9 @@ static int _dpu_encoder_phys_cmd_wait_for_idle(
|
||||
wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
|
||||
wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
|
||||
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_PINGPONG,
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc,
|
||||
phys_enc->irq[INTR_IDX_PINGPONG],
|
||||
dpu_encoder_phys_cmd_pp_tx_done_irq,
|
||||
&wait_info);
|
||||
if (ret == -ETIMEDOUT)
|
||||
_dpu_encoder_phys_cmd_handle_ppdone_timeout(phys_enc);
|
||||
@ -258,10 +262,13 @@ static int dpu_encoder_phys_cmd_control_vblank_irq(
|
||||
enable ? "true" : "false", refcount);
|
||||
|
||||
if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
|
||||
ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
|
||||
ret = dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_RDPTR],
|
||||
dpu_encoder_phys_cmd_pp_rd_ptr_irq,
|
||||
phys_enc);
|
||||
else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
|
||||
ret = dpu_encoder_helper_unregister_irq(phys_enc,
|
||||
INTR_IDX_RDPTR);
|
||||
ret = dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_RDPTR]);
|
||||
|
||||
end:
|
||||
if (ret) {
|
||||
@ -282,21 +289,31 @@ static void dpu_encoder_phys_cmd_irq_control(struct dpu_encoder_phys *phys_enc,
|
||||
enable, atomic_read(&phys_enc->vblank_refcount));
|
||||
|
||||
if (enable) {
|
||||
dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_PINGPONG);
|
||||
dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
|
||||
dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_PINGPONG],
|
||||
dpu_encoder_phys_cmd_pp_tx_done_irq,
|
||||
phys_enc);
|
||||
dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN],
|
||||
dpu_encoder_phys_cmd_underrun_irq,
|
||||
phys_enc);
|
||||
dpu_encoder_phys_cmd_control_vblank_irq(phys_enc, true);
|
||||
|
||||
if (dpu_encoder_phys_cmd_is_master(phys_enc))
|
||||
dpu_encoder_helper_register_irq(phys_enc,
|
||||
INTR_IDX_CTL_START);
|
||||
dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_CTL_START],
|
||||
dpu_encoder_phys_cmd_ctl_start_irq,
|
||||
phys_enc);
|
||||
} else {
|
||||
if (dpu_encoder_phys_cmd_is_master(phys_enc))
|
||||
dpu_encoder_helper_unregister_irq(phys_enc,
|
||||
INTR_IDX_CTL_START);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_CTL_START]);
|
||||
|
||||
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN]);
|
||||
dpu_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
|
||||
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_PINGPONG);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_PINGPONG]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,6 +505,7 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_encoder_phys_cmd *cmd_enc =
|
||||
to_dpu_encoder_phys_cmd(phys_enc);
|
||||
struct dpu_hw_ctl *ctl;
|
||||
|
||||
if (!phys_enc->hw_pp) {
|
||||
DPU_ERROR("invalid encoder\n");
|
||||
@ -504,6 +522,17 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
|
||||
|
||||
if (phys_enc->hw_pp->ops.enable_tearcheck)
|
||||
phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, false);
|
||||
|
||||
if (phys_enc->hw_intf->ops.bind_pingpong_blk) {
|
||||
phys_enc->hw_intf->ops.bind_pingpong_blk(
|
||||
phys_enc->hw_intf,
|
||||
false,
|
||||
phys_enc->hw_pp->idx);
|
||||
|
||||
ctl = phys_enc->hw_ctl;
|
||||
ctl->ops.update_pending_flush_intf(ctl, phys_enc->intf_idx);
|
||||
}
|
||||
|
||||
phys_enc->enable_state = DPU_ENC_DISABLED;
|
||||
}
|
||||
|
||||
@ -623,7 +652,9 @@ static int _dpu_encoder_phys_cmd_wait_for_ctl_start(
|
||||
wait_info.atomic_cnt = &phys_enc->pending_ctlstart_cnt;
|
||||
wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
|
||||
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START,
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc,
|
||||
phys_enc->irq[INTR_IDX_CTL_START],
|
||||
dpu_encoder_phys_cmd_ctl_start_irq,
|
||||
&wait_info);
|
||||
if (ret == -ETIMEDOUT) {
|
||||
DPU_ERROR_CMDENC(cmd_enc, "ctl start interrupt wait failed\n");
|
||||
@ -681,7 +712,9 @@ static int dpu_encoder_phys_cmd_wait_for_vblank(
|
||||
|
||||
atomic_inc(&cmd_enc->pending_vblank_cnt);
|
||||
|
||||
rc = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_RDPTR,
|
||||
rc = dpu_encoder_helper_wait_for_irq(phys_enc,
|
||||
phys_enc->irq[INTR_IDX_RDPTR],
|
||||
dpu_encoder_phys_cmd_pp_rd_ptr_irq,
|
||||
&wait_info);
|
||||
|
||||
return rc;
|
||||
@ -731,7 +764,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
|
||||
{
|
||||
struct dpu_encoder_phys *phys_enc = NULL;
|
||||
struct dpu_encoder_phys_cmd *cmd_enc = NULL;
|
||||
struct dpu_encoder_irq *irq;
|
||||
int i, ret = 0;
|
||||
|
||||
DPU_DEBUG("intf %d\n", p->intf_idx - INTF_0);
|
||||
@ -755,32 +787,8 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
|
||||
phys_enc->enc_spinlock = p->enc_spinlock;
|
||||
cmd_enc->stream_sel = 0;
|
||||
phys_enc->enable_state = DPU_ENC_DISABLED;
|
||||
for (i = 0; i < INTR_IDX_MAX; i++) {
|
||||
irq = &phys_enc->irq[i];
|
||||
INIT_LIST_HEAD(&irq->cb.list);
|
||||
irq->irq_idx = -EINVAL;
|
||||
irq->cb.arg = phys_enc;
|
||||
}
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_CTL_START];
|
||||
irq->name = "ctl_start";
|
||||
irq->intr_idx = INTR_IDX_CTL_START;
|
||||
irq->cb.func = dpu_encoder_phys_cmd_ctl_start_irq;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_PINGPONG];
|
||||
irq->name = "pp_done";
|
||||
irq->intr_idx = INTR_IDX_PINGPONG;
|
||||
irq->cb.func = dpu_encoder_phys_cmd_pp_tx_done_irq;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_RDPTR];
|
||||
irq->name = "pp_rd_ptr";
|
||||
irq->intr_idx = INTR_IDX_RDPTR;
|
||||
irq->cb.func = dpu_encoder_phys_cmd_pp_rd_ptr_irq;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
|
||||
irq->name = "underrun";
|
||||
irq->intr_idx = INTR_IDX_UNDERRUN;
|
||||
irq->cb.func = dpu_encoder_phys_cmd_underrun_irq;
|
||||
for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
|
||||
phys_enc->irq[i] = -EINVAL;
|
||||
|
||||
atomic_set(&phys_enc->vblank_refcount, 0);
|
||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||
|
@ -91,25 +91,27 @@ static void drm_mode_to_intf_timing_params(
|
||||
timing->vsync_polarity = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For edp only:
|
||||
* DISPLAY_V_START = (VBP * HCYCLE) + HBP
|
||||
* DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP
|
||||
*/
|
||||
/*
|
||||
* if (vid_enc->hw->cap->type == INTF_EDP) {
|
||||
* display_v_start += mode->htotal - mode->hsync_start;
|
||||
* display_v_end -= mode->hsync_start - mode->hdisplay;
|
||||
* }
|
||||
*/
|
||||
/* for DP/EDP, Shift timings to align it to bottom right */
|
||||
if ((phys_enc->hw_intf->cap->type == INTF_DP) ||
|
||||
(phys_enc->hw_intf->cap->type == INTF_EDP)) {
|
||||
if (phys_enc->hw_intf->cap->type == INTF_DP) {
|
||||
timing->h_back_porch += timing->h_front_porch;
|
||||
timing->h_front_porch = 0;
|
||||
timing->v_back_porch += timing->v_front_porch;
|
||||
timing->v_front_porch = 0;
|
||||
}
|
||||
|
||||
timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent);
|
||||
|
||||
/*
|
||||
* for DP, divide the horizonal parameters by 2 when
|
||||
* widebus is enabled
|
||||
*/
|
||||
if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) {
|
||||
timing->width = timing->width >> 1;
|
||||
timing->xres = timing->xres >> 1;
|
||||
timing->h_back_porch = timing->h_back_porch >> 1;
|
||||
timing->h_front_porch = timing->h_front_porch >> 1;
|
||||
timing->hsync_pulse_width = timing->hsync_pulse_width >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 get_horizontal_total(const struct intf_timing_params *timing)
|
||||
@ -353,13 +355,9 @@ static void dpu_encoder_phys_vid_atomic_mode_set(
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct dpu_encoder_irq *irq;
|
||||
phys_enc->irq[INTR_IDX_VSYNC] = phys_enc->hw_intf->cap->intr_vsync;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_VSYNC];
|
||||
irq->irq_idx = phys_enc->hw_intf->cap->intr_vsync;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
|
||||
irq->irq_idx = phys_enc->hw_intf->cap->intr_underrun;
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN] = phys_enc->hw_intf->cap->intr_underrun;
|
||||
}
|
||||
|
||||
static int dpu_encoder_phys_vid_control_vblank_irq(
|
||||
@ -385,10 +383,13 @@ static int dpu_encoder_phys_vid_control_vblank_irq(
|
||||
atomic_read(&phys_enc->vblank_refcount));
|
||||
|
||||
if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
|
||||
ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC);
|
||||
ret = dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_VSYNC],
|
||||
dpu_encoder_phys_vid_vblank_irq,
|
||||
phys_enc);
|
||||
else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
|
||||
ret = dpu_encoder_helper_unregister_irq(phys_enc,
|
||||
INTR_IDX_VSYNC);
|
||||
ret = dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_VSYNC]);
|
||||
|
||||
end:
|
||||
if (ret) {
|
||||
@ -461,7 +462,9 @@ static int dpu_encoder_phys_vid_wait_for_vblank(
|
||||
}
|
||||
|
||||
/* Wait for kickoff to complete */
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc,
|
||||
phys_enc->irq[INTR_IDX_VSYNC],
|
||||
dpu_encoder_phys_vid_vblank_irq,
|
||||
&wait_info);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
@ -513,7 +516,8 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff(
|
||||
DPU_ERROR_VIDENC(phys_enc, "ctl %d reset failure: %d\n",
|
||||
ctl->idx, rc);
|
||||
msm_disp_snapshot_state(drm_enc->dev);
|
||||
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_VSYNC);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_VSYNC]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,10 +606,14 @@ static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc,
|
||||
if (WARN_ON(ret))
|
||||
return;
|
||||
|
||||
dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
|
||||
dpu_core_irq_register_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN],
|
||||
dpu_encoder_phys_vid_underrun_irq,
|
||||
phys_enc);
|
||||
} else {
|
||||
dpu_encoder_phys_vid_control_vblank_irq(phys_enc, false);
|
||||
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
|
||||
dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
|
||||
phys_enc->irq[INTR_IDX_UNDERRUN]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -669,7 +677,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
|
||||
struct dpu_enc_phys_init_params *p)
|
||||
{
|
||||
struct dpu_encoder_phys *phys_enc = NULL;
|
||||
struct dpu_encoder_irq *irq;
|
||||
int i;
|
||||
|
||||
if (!p) {
|
||||
@ -695,22 +702,8 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
|
||||
phys_enc->split_role = p->split_role;
|
||||
phys_enc->intf_mode = INTF_MODE_VIDEO;
|
||||
phys_enc->enc_spinlock = p->enc_spinlock;
|
||||
for (i = 0; i < INTR_IDX_MAX; i++) {
|
||||
irq = &phys_enc->irq[i];
|
||||
INIT_LIST_HEAD(&irq->cb.list);
|
||||
irq->irq_idx = -EINVAL;
|
||||
irq->cb.arg = phys_enc;
|
||||
}
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_VSYNC];
|
||||
irq->name = "vsync_irq";
|
||||
irq->intr_idx = INTR_IDX_VSYNC;
|
||||
irq->cb.func = dpu_encoder_phys_vid_vblank_irq;
|
||||
|
||||
irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
|
||||
irq->name = "underrun";
|
||||
irq->intr_idx = INTR_IDX_UNDERRUN;
|
||||
irq->cb.func = dpu_encoder_phys_vid_underrun_irq;
|
||||
for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
|
||||
phys_enc->irq[i] = -EINVAL;
|
||||
|
||||
atomic_set(&phys_enc->vblank_refcount, 0);
|
||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||
|
753
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
Normal file
753
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
Normal file
@ -0,0 +1,753 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "dpu_encoder_phys.h"
|
||||
#include "dpu_formats.h"
|
||||
#include "dpu_hw_top.h"
|
||||
#include "dpu_hw_wb.h"
|
||||
#include "dpu_hw_lm.h"
|
||||
#include "dpu_hw_blk.h"
|
||||
#include "dpu_hw_merge3d.h"
|
||||
#include "dpu_hw_interrupts.h"
|
||||
#include "dpu_core_irq.h"
|
||||
#include "dpu_vbif.h"
|
||||
#include "dpu_crtc.h"
|
||||
#include "disp/msm_disp_snapshot.h"
|
||||
|
||||
#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
|
||||
|
||||
#define to_dpu_encoder_phys_wb(x) \
|
||||
container_of(x, struct dpu_encoder_phys_wb, base)
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_is_master - report wb always as master encoder
|
||||
*/
|
||||
static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
/* there is only one physical enc for dpu_writeback */
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback interface
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_set_ot_limit(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
|
||||
struct dpu_vbif_set_ot_params ot_params;
|
||||
|
||||
memset(&ot_params, 0, sizeof(ot_params));
|
||||
ot_params.xin_id = hw_wb->caps->xin_id;
|
||||
ot_params.num = hw_wb->idx - WB_0;
|
||||
ot_params.width = phys_enc->cached_mode.hdisplay;
|
||||
ot_params.height = phys_enc->cached_mode.vdisplay;
|
||||
ot_params.is_wfd = true;
|
||||
ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
|
||||
ot_params.vbif_idx = hw_wb->caps->vbif_idx;
|
||||
ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
|
||||
ot_params.rd = false;
|
||||
|
||||
dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_set_qos_remap(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_vbif_set_qos_params qos_params;
|
||||
|
||||
if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
|
||||
DPU_ERROR("invalid arguments\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
|
||||
DPU_ERROR("invalid writeback hardware\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hw_wb = phys_enc->hw_wb;
|
||||
|
||||
memset(&qos_params, 0, sizeof(qos_params));
|
||||
qos_params.vbif_idx = hw_wb->caps->vbif_idx;
|
||||
qos_params.xin_id = hw_wb->caps->xin_id;
|
||||
qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
|
||||
qos_params.num = hw_wb->idx - WB_0;
|
||||
qos_params.is_rt = false;
|
||||
|
||||
DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
|
||||
qos_params.num,
|
||||
qos_params.vbif_idx,
|
||||
qos_params.xin_id, qos_params.is_rt);
|
||||
|
||||
dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_hw_wb_qos_cfg qos_cfg;
|
||||
struct dpu_mdss_cfg *catalog;
|
||||
struct dpu_qos_lut_tbl *qos_lut_tb;
|
||||
|
||||
if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
|
||||
DPU_ERROR("invalid parameter(s)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
catalog = phys_enc->dpu_kms->catalog;
|
||||
|
||||
hw_wb = phys_enc->hw_wb;
|
||||
|
||||
memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
|
||||
qos_cfg.danger_safe_en = true;
|
||||
qos_cfg.danger_lut =
|
||||
catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
|
||||
|
||||
qos_cfg.safe_lut = catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
|
||||
|
||||
qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
|
||||
qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
|
||||
|
||||
if (hw_wb->ops.setup_qos_lut)
|
||||
hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_setup_fb - setup output framebuffer
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
* @fb: Pointer to output framebuffer
|
||||
* @wb_roi: Pointer to output region of interest
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_hw_wb_cfg *wb_cfg;
|
||||
struct dpu_hw_cdp_cfg cdp_cfg;
|
||||
|
||||
if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
|
||||
DPU_ERROR("invalid encoder\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hw_wb = phys_enc->hw_wb;
|
||||
wb_cfg = &wb_enc->wb_cfg;
|
||||
|
||||
wb_cfg->intf_mode = phys_enc->intf_mode;
|
||||
wb_cfg->roi.x1 = 0;
|
||||
wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
|
||||
wb_cfg->roi.y1 = 0;
|
||||
wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
|
||||
|
||||
if (hw_wb->ops.setup_roi)
|
||||
hw_wb->ops.setup_roi(hw_wb, wb_cfg);
|
||||
|
||||
if (hw_wb->ops.setup_outformat)
|
||||
hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
|
||||
|
||||
if (hw_wb->ops.setup_cdp) {
|
||||
memset(&cdp_cfg, 0, sizeof(struct dpu_hw_cdp_cfg));
|
||||
|
||||
cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
|
||||
[DPU_PERF_CDP_USAGE_NRT].wr_enable;
|
||||
cdp_cfg.ubwc_meta_enable =
|
||||
DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
|
||||
cdp_cfg.tile_amortize_enable =
|
||||
DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
|
||||
DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
|
||||
cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
|
||||
|
||||
hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
|
||||
}
|
||||
|
||||
if (hw_wb->ops.setup_outaddress)
|
||||
hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
|
||||
* @phys_enc:Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_hw_ctl *ctl;
|
||||
|
||||
if (!phys_enc) {
|
||||
DPU_ERROR("invalid encoder\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hw_wb = phys_enc->hw_wb;
|
||||
ctl = phys_enc->hw_ctl;
|
||||
|
||||
if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
|
||||
(phys_enc->hw_ctl &&
|
||||
phys_enc->hw_ctl->ops.setup_intf_cfg)) {
|
||||
struct dpu_hw_intf_cfg intf_cfg = {0};
|
||||
struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
|
||||
enum dpu_3d_blend_mode mode_3d;
|
||||
|
||||
mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
|
||||
|
||||
intf_cfg.intf = DPU_NONE;
|
||||
intf_cfg.wb = hw_wb->idx;
|
||||
|
||||
if (mode_3d && hw_pp && hw_pp->merge_3d)
|
||||
intf_cfg.merge_3d = hw_pp->merge_3d->idx;
|
||||
|
||||
if (phys_enc->hw_pp->merge_3d && phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
|
||||
phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
|
||||
mode_3d);
|
||||
|
||||
/* setup which pp blk will connect to this wb */
|
||||
if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
|
||||
phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, true,
|
||||
phys_enc->hw_pp->idx);
|
||||
|
||||
phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
|
||||
} else if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg) {
|
||||
struct dpu_hw_intf_cfg intf_cfg = {0};
|
||||
|
||||
intf_cfg.intf = DPU_NONE;
|
||||
intf_cfg.wb = hw_wb->idx;
|
||||
intf_cfg.mode_3d =
|
||||
dpu_encoder_helper_get_3d_blend_mode(phys_enc);
|
||||
phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic states
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
* @crtc_state: Pointer to CRTC atomic state
|
||||
* @conn_state: Pointer to connector atomic state
|
||||
*/
|
||||
static int dpu_encoder_phys_wb_atomic_check(
|
||||
struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct drm_framebuffer *fb;
|
||||
const struct drm_display_mode *mode = &crtc_state->mode;
|
||||
|
||||
DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
|
||||
phys_enc->wb_idx, mode->name, mode->hdisplay, mode->vdisplay);
|
||||
|
||||
if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
|
||||
return 0;
|
||||
|
||||
fb = conn_state->writeback_job->fb;
|
||||
|
||||
if (!conn_state || !conn_state->connector) {
|
||||
DPU_ERROR("invalid connector state\n");
|
||||
return -EINVAL;
|
||||
} else if (conn_state->connector->status !=
|
||||
connector_status_connected) {
|
||||
DPU_ERROR("connector not connected %d\n",
|
||||
conn_state->connector->status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
|
||||
fb->width, fb->height);
|
||||
|
||||
if (fb->width != mode->hdisplay) {
|
||||
DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
|
||||
mode->hdisplay);
|
||||
return -EINVAL;
|
||||
} else if (fb->height != mode->vdisplay) {
|
||||
DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
|
||||
mode->vdisplay);
|
||||
return -EINVAL;
|
||||
} else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
|
||||
DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
|
||||
fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _dpu_encoder_phys_wb_update_flush - flush hardware update
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void _dpu_encoder_phys_wb_update_flush(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb;
|
||||
struct dpu_hw_ctl *hw_ctl;
|
||||
struct dpu_hw_pingpong *hw_pp;
|
||||
u32 pending_flush = 0;
|
||||
|
||||
if (!phys_enc)
|
||||
return;
|
||||
|
||||
hw_wb = phys_enc->hw_wb;
|
||||
hw_pp = phys_enc->hw_pp;
|
||||
hw_ctl = phys_enc->hw_ctl;
|
||||
|
||||
DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
|
||||
|
||||
if (!hw_ctl) {
|
||||
DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hw_ctl->ops.update_pending_flush_wb)
|
||||
hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
|
||||
|
||||
if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && hw_pp->merge_3d)
|
||||
hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
|
||||
hw_pp->merge_3d->idx);
|
||||
|
||||
if (hw_ctl->ops.get_pending_flush)
|
||||
pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
|
||||
|
||||
DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
|
||||
hw_ctl->idx - CTL_0, pending_flush,
|
||||
hw_wb->idx - WB_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_setup - setup writeback encoder
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_setup(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
|
||||
struct drm_display_mode mode = phys_enc->cached_mode;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
|
||||
DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
|
||||
hw_wb->idx - WB_0, mode.name,
|
||||
mode.hdisplay, mode.vdisplay);
|
||||
|
||||
dpu_encoder_phys_wb_set_ot_limit(phys_enc);
|
||||
|
||||
dpu_encoder_phys_wb_set_qos_remap(phys_enc);
|
||||
|
||||
dpu_encoder_phys_wb_set_qos(phys_enc);
|
||||
|
||||
dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
|
||||
|
||||
dpu_encoder_phys_wb_setup_cdp(phys_enc);
|
||||
|
||||
}
|
||||
|
||||
static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
|
||||
{
|
||||
struct dpu_encoder_phys *phys_enc = arg;
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
|
||||
struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
|
||||
unsigned long lock_flags;
|
||||
u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
|
||||
|
||||
DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
|
||||
|
||||
if (phys_enc->parent_ops->handle_frame_done)
|
||||
phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
|
||||
phys_enc, event);
|
||||
|
||||
if (phys_enc->parent_ops->handle_vblank_virt)
|
||||
phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
|
||||
phys_enc);
|
||||
|
||||
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
|
||||
atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
|
||||
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
|
||||
|
||||
if (wb_enc->wb_conn)
|
||||
drm_writeback_signal_completion(wb_enc->wb_conn, 0);
|
||||
|
||||
/* Signal any waiting atomic commit thread */
|
||||
wake_up_all(&phys_enc->pending_kickoff_wq);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_done_irq - writeback interrupt handler
|
||||
* @arg: Pointer to writeback encoder
|
||||
* @irq_idx: interrupt index
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
|
||||
{
|
||||
_dpu_encoder_phys_wb_frame_done_helper(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_irq_ctrl - irq control of WB
|
||||
* @phys: Pointer to physical encoder
|
||||
* @enable: indicates enable or disable interrupts
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_irq_ctrl(
|
||||
struct dpu_encoder_phys *phys, bool enable)
|
||||
{
|
||||
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
|
||||
|
||||
if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1)
|
||||
dpu_core_irq_register_callback(phys->dpu_kms,
|
||||
phys->irq[INTR_IDX_WB_DONE], dpu_encoder_phys_wb_done_irq, phys);
|
||||
else if (!enable &&
|
||||
atomic_dec_return(&wb_enc->wbirq_refcount) == 0)
|
||||
dpu_core_irq_unregister_callback(phys->dpu_kms, phys->irq[INTR_IDX_WB_DONE]);
|
||||
}
|
||||
|
||||
static void dpu_encoder_phys_wb_atomic_mode_set(
|
||||
struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
|
||||
phys_enc->irq[INTR_IDX_WB_DONE] = phys_enc->hw_wb->caps->intr_wb_done;
|
||||
}
|
||||
|
||||
static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
|
||||
|
||||
wb_enc->wb_done_timeout_cnt++;
|
||||
|
||||
if (wb_enc->wb_done_timeout_cnt == 1)
|
||||
msm_disp_snapshot_state(phys_enc->parent->dev);
|
||||
|
||||
atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
|
||||
|
||||
/* request a ctl reset before the next kickoff */
|
||||
phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
|
||||
|
||||
if (wb_enc->wb_conn)
|
||||
drm_writeback_signal_completion(wb_enc->wb_conn, 0);
|
||||
|
||||
if (phys_enc->parent_ops->handle_frame_done)
|
||||
phys_enc->parent_ops->handle_frame_done(
|
||||
phys_enc->parent, phys_enc, frame_event);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_wait_for_commit_done - wait until request is committed
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static int dpu_encoder_phys_wb_wait_for_commit_done(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
unsigned long ret;
|
||||
struct dpu_encoder_wait_info wait_info;
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
|
||||
wait_info.wq = &phys_enc->pending_kickoff_wq;
|
||||
wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
|
||||
wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
|
||||
|
||||
ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
|
||||
dpu_encoder_phys_wb_done_irq, &wait_info);
|
||||
if (ret == -ETIMEDOUT)
|
||||
_dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
|
||||
else if (!ret)
|
||||
wb_enc->wb_done_timeout_cnt = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
* Returns: Zero on success
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_prepare_for_kickoff(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
struct drm_connector *drm_conn;
|
||||
struct drm_connector_state *state;
|
||||
|
||||
DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
|
||||
|
||||
if (!wb_enc->wb_conn || !wb_enc->wb_job) {
|
||||
DPU_ERROR("invalid wb_conn or wb_job\n");
|
||||
return;
|
||||
}
|
||||
|
||||
drm_conn = &wb_enc->wb_conn->base;
|
||||
state = drm_conn->state;
|
||||
|
||||
if (wb_enc->wb_conn && wb_enc->wb_job)
|
||||
drm_writeback_queue_job(wb_enc->wb_conn, state);
|
||||
|
||||
dpu_encoder_phys_wb_setup(phys_enc);
|
||||
|
||||
_dpu_encoder_phys_wb_update_flush(phys_enc);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static bool dpu_encoder_phys_wb_needs_single_flush(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_handle_post_kickoff(
|
||||
struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_enable - enable writeback encoder
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
|
||||
phys_enc->enable_state = DPU_ENC_ENABLED;
|
||||
}
|
||||
/**
|
||||
* dpu_encoder_phys_wb_disable - disable writeback encoder
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
|
||||
struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
|
||||
|
||||
DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
|
||||
|
||||
if (phys_enc->enable_state == DPU_ENC_DISABLED) {
|
||||
DPU_ERROR("encoder is already disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* reset h/w before final flush */
|
||||
if (phys_enc->hw_ctl->ops.clear_pending_flush)
|
||||
phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
|
||||
|
||||
/*
|
||||
* New CTL reset sequence from 5.0 MDP onwards.
|
||||
* If has_3d_merge_reset is not set, legacy reset
|
||||
* sequence is executed.
|
||||
*
|
||||
* Legacy reset sequence has not been implemented yet.
|
||||
* Any target earlier than SM8150 will need it and when
|
||||
* WB support is added to those targets will need to add
|
||||
* the legacy teardown sequence as well.
|
||||
*/
|
||||
if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
|
||||
dpu_encoder_helper_phys_cleanup(phys_enc);
|
||||
|
||||
phys_enc->enable_state = DPU_ENC_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_destroy - destroy writeback encoder
|
||||
* @phys_enc: Pointer to physical encoder
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
DPU_DEBUG("[wb:%d]\n", phys_enc->wb_idx - WB_0);
|
||||
|
||||
if (!phys_enc)
|
||||
return;
|
||||
|
||||
kfree(phys_enc);
|
||||
}
|
||||
|
||||
static void dpu_encoder_phys_wb_prepare_wb_job(struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
const struct msm_format *format;
|
||||
struct msm_gem_address_space *aspace;
|
||||
struct dpu_hw_wb_cfg *wb_cfg;
|
||||
int ret;
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
|
||||
if (!job->fb)
|
||||
return;
|
||||
|
||||
wb_enc->wb_job = job;
|
||||
wb_enc->wb_conn = job->connector;
|
||||
aspace = phys_enc->dpu_kms->base.aspace;
|
||||
|
||||
wb_cfg = &wb_enc->wb_cfg;
|
||||
|
||||
memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
|
||||
|
||||
ret = msm_framebuffer_prepare(job->fb, aspace, false);
|
||||
if (ret) {
|
||||
DPU_ERROR("prep fb failed, %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
format = msm_framebuffer_format(job->fb);
|
||||
|
||||
wb_cfg->dest.format = dpu_get_dpu_format_ext(
|
||||
format->pixel_format, job->fb->modifier);
|
||||
if (!wb_cfg->dest.format) {
|
||||
/* this error should be detected during atomic_check */
|
||||
DPU_ERROR("failed to get format %x\n", format->pixel_format);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
|
||||
if (ret) {
|
||||
DPU_DEBUG("failed to populate layout %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
wb_cfg->dest.width = job->fb->width;
|
||||
wb_cfg->dest.height = job->fb->height;
|
||||
wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
|
||||
|
||||
if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
|
||||
(wb_cfg->dest.format->element[0] == C1_B_Cb))
|
||||
swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
|
||||
|
||||
DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
|
||||
wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
|
||||
wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
|
||||
|
||||
DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
|
||||
wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
|
||||
wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
|
||||
}
|
||||
|
||||
static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
struct msm_gem_address_space *aspace;
|
||||
|
||||
if (!job->fb)
|
||||
return;
|
||||
|
||||
aspace = phys_enc->dpu_kms->base.aspace;
|
||||
|
||||
msm_framebuffer_cleanup(job->fb, aspace, false);
|
||||
wb_enc->wb_job = NULL;
|
||||
wb_enc->wb_conn = NULL;
|
||||
}
|
||||
|
||||
static bool dpu_encoder_phys_wb_is_valid_for_commit(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
|
||||
|
||||
if (wb_enc->wb_job)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_init_ops - initialize writeback operations
|
||||
* @ops: Pointer to encoder operation table
|
||||
*/
|
||||
static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
|
||||
{
|
||||
ops->is_master = dpu_encoder_phys_wb_is_master;
|
||||
ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
|
||||
ops->enable = dpu_encoder_phys_wb_enable;
|
||||
ops->disable = dpu_encoder_phys_wb_disable;
|
||||
ops->destroy = dpu_encoder_phys_wb_destroy;
|
||||
ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
|
||||
ops->wait_for_commit_done = dpu_encoder_phys_wb_wait_for_commit_done;
|
||||
ops->prepare_for_kickoff = dpu_encoder_phys_wb_prepare_for_kickoff;
|
||||
ops->handle_post_kickoff = dpu_encoder_phys_wb_handle_post_kickoff;
|
||||
ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
|
||||
ops->trigger_start = dpu_encoder_helper_trigger_start;
|
||||
ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
|
||||
ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
|
||||
ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
|
||||
ops->is_valid_for_commit = dpu_encoder_phys_wb_is_valid_for_commit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_encoder_phys_wb_init - initialize writeback encoder
|
||||
* @init: Pointer to init info structure with initialization params
|
||||
*/
|
||||
struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
|
||||
struct dpu_enc_phys_init_params *p)
|
||||
{
|
||||
struct dpu_encoder_phys *phys_enc = NULL;
|
||||
struct dpu_encoder_phys_wb *wb_enc = NULL;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
DPU_DEBUG("\n");
|
||||
|
||||
if (!p || !p->parent) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
ret = -EINVAL;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
|
||||
if (!wb_enc) {
|
||||
DPU_ERROR("failed to allocate wb phys_enc enc\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
phys_enc = &wb_enc->base;
|
||||
phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
|
||||
phys_enc->wb_idx = p->wb_idx;
|
||||
|
||||
dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
|
||||
phys_enc->parent = p->parent;
|
||||
phys_enc->parent_ops = p->parent_ops;
|
||||
phys_enc->dpu_kms = p->dpu_kms;
|
||||
phys_enc->split_role = p->split_role;
|
||||
phys_enc->intf_mode = INTF_MODE_WB_LINE;
|
||||
phys_enc->wb_idx = p->wb_idx;
|
||||
phys_enc->enc_spinlock = p->enc_spinlock;
|
||||
|
||||
atomic_set(&wb_enc->wbirq_refcount, 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
|
||||
phys_enc->irq[i] = -EINVAL;
|
||||
|
||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||
atomic_set(&phys_enc->vblank_refcount, 0);
|
||||
wb_enc->wb_done_timeout_cnt = 0;
|
||||
|
||||
init_waitqueue_head(&phys_enc->pending_kickoff_wq);
|
||||
phys_enc->enable_state = DPU_ENC_DISABLED;
|
||||
|
||||
DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
|
||||
phys_enc->wb_idx);
|
||||
|
||||
return phys_enc;
|
||||
|
||||
fail_alloc:
|
||||
return ERR_PTR(ret);
|
||||
}
|
@ -20,6 +20,28 @@ const struct dpu_format *dpu_get_dpu_format_ext(
|
||||
|
||||
#define dpu_get_dpu_format(f) dpu_get_dpu_format_ext(f, 0)
|
||||
|
||||
/**
|
||||
* dpu_find_format - validate if the pixel format is supported
|
||||
* @format: dpu format
|
||||
* @supported_formats: supported formats by dpu HW
|
||||
* @num_formatss: total number of formats
|
||||
*
|
||||
* Return: false if not valid format, true on success
|
||||
*/
|
||||
static inline bool dpu_find_format(u32 format, const u32 *supported_formats,
|
||||
size_t num_formats)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_formats; i++) {
|
||||
/* check for valid formats supported */
|
||||
if (format == supported_formats[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_get_msm_format - get an dpu_format by its msm_format base
|
||||
* callback function registers with the msm_kms layer
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
|
||||
@ -35,6 +36,9 @@
|
||||
BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_TS_PREFILL_REC1) |\
|
||||
BIT(DPU_SSPP_CDP) | BIT(DPU_SSPP_EXCL_RECT))
|
||||
|
||||
#define VIG_SC7280_MASK \
|
||||
(VIG_SC7180_MASK | BIT(DPU_SSPP_INLINE_ROTATION))
|
||||
|
||||
#define DMA_SDM845_MASK \
|
||||
(BIT(DPU_SSPP_SRC) | BIT(DPU_SSPP_QOS) | BIT(DPU_SSPP_QOS_8LVL) |\
|
||||
BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_TS_PREFILL_REC1) |\
|
||||
@ -117,6 +121,16 @@
|
||||
BIT(MDP_AD4_0_INTR) | \
|
||||
BIT(MDP_AD4_1_INTR))
|
||||
|
||||
#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \
|
||||
BIT(DPU_WB_UBWC) | \
|
||||
BIT(DPU_WB_YUV_CONFIG) | \
|
||||
BIT(DPU_WB_PIPE_ALPHA) | \
|
||||
BIT(DPU_WB_XY_ROI_OFFSET) | \
|
||||
BIT(DPU_WB_QOS) | \
|
||||
BIT(DPU_WB_QOS_8LVL) | \
|
||||
BIT(DPU_WB_CDP) | \
|
||||
BIT(DPU_WB_INPUT_CTRL))
|
||||
|
||||
#define DEFAULT_PIXEL_RAM_SIZE (50 * 1024)
|
||||
#define DEFAULT_DPU_LINE_WIDTH 2048
|
||||
#define DEFAULT_DPU_OUTPUT_LINE_WIDTH 2560
|
||||
@ -203,6 +217,45 @@ static const uint32_t plane_formats_yuv[] = {
|
||||
DRM_FORMAT_YVU420,
|
||||
};
|
||||
|
||||
static const u32 rotation_v2_formats[] = {
|
||||
DRM_FORMAT_NV12,
|
||||
/* TODO add formats after validation */
|
||||
};
|
||||
|
||||
static const uint32_t wb2_formats[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_RGBA8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGBX8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_RGBA5551,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_RGBX5551,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_RGBA4444,
|
||||
DRM_FORMAT_RGBX4444,
|
||||
DRM_FORMAT_XRGB4444,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_BGRA8888,
|
||||
DRM_FORMAT_BGRX8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_BGRA5551,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_BGRX5551,
|
||||
DRM_FORMAT_ABGR4444,
|
||||
DRM_FORMAT_BGRA4444,
|
||||
DRM_FORMAT_BGRX4444,
|
||||
DRM_FORMAT_XBGR4444,
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* DPU sub blocks config
|
||||
*************************************************************/
|
||||
@ -223,6 +276,17 @@ static const struct dpu_caps msm8998_dpu_caps = {
|
||||
.max_vdeci_exp = MAX_VERT_DECIMATION,
|
||||
};
|
||||
|
||||
static const struct dpu_caps qcm2290_dpu_caps = {
|
||||
.max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
|
||||
.max_mixer_blendstages = 0x4,
|
||||
.smart_dma_rev = DPU_SSPP_SMART_DMA_V2,
|
||||
.ubwc_version = DPU_HW_UBWC_VER_20,
|
||||
.has_dim_layer = true,
|
||||
.has_idle_pc = true,
|
||||
.max_linewidth = 2160,
|
||||
.pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
|
||||
};
|
||||
|
||||
static const struct dpu_caps sdm845_dpu_caps = {
|
||||
.max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
|
||||
.max_mixer_blendstages = 0xb,
|
||||
@ -338,17 +402,6 @@ static const struct dpu_mdp_cfg msm8998_mdp[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct dpu_caps qcm2290_dpu_caps = {
|
||||
.max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
|
||||
.max_mixer_blendstages = 0x4,
|
||||
.smart_dma_rev = DPU_SSPP_SMART_DMA_V2,
|
||||
.ubwc_version = DPU_HW_UBWC_VER_20,
|
||||
.has_dim_layer = true,
|
||||
.has_idle_pc = true,
|
||||
.max_linewidth = 2160,
|
||||
.pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
|
||||
};
|
||||
|
||||
static const struct dpu_mdp_cfg sdm845_mdp[] = {
|
||||
{
|
||||
.name = "top_0", .id = MDP_TOP,
|
||||
@ -440,6 +493,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = {
|
||||
.reg_off = 0x2C4, .bit_off = 8},
|
||||
.clk_ctrls[DPU_CLK_CTRL_REG_DMA] = {
|
||||
.reg_off = 0x2BC, .bit_off = 20},
|
||||
.clk_ctrls[DPU_CLK_CTRL_WB2] = {
|
||||
.reg_off = 0x3B8, .bit_off = 24},
|
||||
},
|
||||
};
|
||||
|
||||
@ -642,7 +697,6 @@ static const struct dpu_ctl_cfg qcm2290_ctl[] = {
|
||||
*************************************************************/
|
||||
|
||||
/* SSPP common configuration */
|
||||
|
||||
#define _VIG_SBLK(num, sdma_pri, qseed_ver) \
|
||||
{ \
|
||||
.maxdwnscale = MAX_DOWNSCALE_RATIO, \
|
||||
@ -660,6 +714,27 @@ static const struct dpu_ctl_cfg qcm2290_ctl[] = {
|
||||
.num_formats = ARRAY_SIZE(plane_formats_yuv), \
|
||||
.virt_format_list = plane_formats, \
|
||||
.virt_num_formats = ARRAY_SIZE(plane_formats), \
|
||||
.rotation_cfg = NULL, \
|
||||
}
|
||||
|
||||
#define _VIG_SBLK_ROT(num, sdma_pri, qseed_ver, rot_cfg) \
|
||||
{ \
|
||||
.maxdwnscale = MAX_DOWNSCALE_RATIO, \
|
||||
.maxupscale = MAX_UPSCALE_RATIO, \
|
||||
.smart_dma_priority = sdma_pri, \
|
||||
.src_blk = {.name = STRCAT("sspp_src_", num), \
|
||||
.id = DPU_SSPP_SRC, .base = 0x00, .len = 0x150,}, \
|
||||
.scaler_blk = {.name = STRCAT("sspp_scaler", num), \
|
||||
.id = qseed_ver, \
|
||||
.base = 0xa00, .len = 0xa0,}, \
|
||||
.csc_blk = {.name = STRCAT("sspp_csc", num), \
|
||||
.id = DPU_SSPP_CSC_10BIT, \
|
||||
.base = 0x1a00, .len = 0x100,}, \
|
||||
.format_list = plane_formats_yuv, \
|
||||
.num_formats = ARRAY_SIZE(plane_formats_yuv), \
|
||||
.virt_format_list = plane_formats, \
|
||||
.virt_num_formats = ARRAY_SIZE(plane_formats), \
|
||||
.rotation_cfg = rot_cfg, \
|
||||
}
|
||||
|
||||
#define _DMA_SBLK(num, sdma_pri) \
|
||||
@ -684,6 +759,12 @@ static const struct dpu_sspp_sub_blks msm8998_vig_sblk_2 =
|
||||
static const struct dpu_sspp_sub_blks msm8998_vig_sblk_3 =
|
||||
_VIG_SBLK("3", 0, DPU_SSPP_SCALER_QSEED3);
|
||||
|
||||
static const struct dpu_rotation_cfg dpu_rot_sc7280_cfg_v2 = {
|
||||
.rot_maxheight = 1088,
|
||||
.rot_num_formats = ARRAY_SIZE(rotation_v2_formats),
|
||||
.rot_format_list = rotation_v2_formats,
|
||||
};
|
||||
|
||||
static const struct dpu_sspp_sub_blks sdm845_vig_sblk_0 =
|
||||
_VIG_SBLK("0", 5, DPU_SSPP_SCALER_QSEED3);
|
||||
static const struct dpu_sspp_sub_blks sdm845_vig_sblk_1 =
|
||||
@ -751,6 +832,9 @@ static const struct dpu_sspp_cfg sdm845_sspp[] = {
|
||||
static const struct dpu_sspp_sub_blks sc7180_vig_sblk_0 =
|
||||
_VIG_SBLK("0", 4, DPU_SSPP_SCALER_QSEED4);
|
||||
|
||||
static const struct dpu_sspp_sub_blks sc7280_vig_sblk_0 =
|
||||
_VIG_SBLK_ROT("0", 4, DPU_SSPP_SCALER_QSEED4, &dpu_rot_sc7280_cfg_v2);
|
||||
|
||||
static const struct dpu_sspp_cfg sc7180_sspp[] = {
|
||||
SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SC7180_MASK,
|
||||
sc7180_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0),
|
||||
@ -791,8 +875,8 @@ static const struct dpu_sspp_cfg sm8250_sspp[] = {
|
||||
};
|
||||
|
||||
static const struct dpu_sspp_cfg sc7280_sspp[] = {
|
||||
SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SC7180_MASK,
|
||||
sc7180_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0),
|
||||
SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SC7280_MASK,
|
||||
sc7280_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0),
|
||||
SSPP_BLK("sspp_8", SSPP_DMA0, 0x24000, DMA_SDM845_MASK,
|
||||
sdm845_dma_sblk_0, 1, SSPP_TYPE_DMA, DPU_CLK_CTRL_DMA0),
|
||||
SSPP_BLK("sspp_9", SSPP_DMA1, 0x26000, DMA_CURSOR_SDM845_MASK,
|
||||
@ -1117,6 +1201,24 @@ static const struct dpu_pingpong_cfg sc7280_pp[] = {
|
||||
PP_BLK("pingpong_2", PINGPONG_2, 0x6b000, 0, sc7280_pp_sblk, -1, -1),
|
||||
PP_BLK("pingpong_3", PINGPONG_3, 0x6c000, 0, sc7280_pp_sblk, -1, -1),
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* DSC sub blocks config
|
||||
*************************************************************/
|
||||
#define DSC_BLK(_name, _id, _base) \
|
||||
{\
|
||||
.name = _name, .id = _id, \
|
||||
.base = _base, .len = 0x140, \
|
||||
.features = 0, \
|
||||
}
|
||||
|
||||
static struct dpu_dsc_cfg sdm845_dsc[] = {
|
||||
DSC_BLK("dsc_0", DSC_0, 0x80000),
|
||||
DSC_BLK("dsc_1", DSC_1, 0x80400),
|
||||
DSC_BLK("dsc_2", DSC_2, 0x80800),
|
||||
DSC_BLK("dsc_3", DSC_3, 0x80c00),
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* INTF sub blocks config
|
||||
*************************************************************/
|
||||
@ -1179,6 +1281,29 @@ static const struct dpu_intf_cfg qcm2290_intf[] = {
|
||||
INTF_BLK("intf_1", INTF_1, 0x6A800, INTF_DSI, 0, 24, INTF_SC7180_MASK, MDP_SSPP_TOP0_INTR, 26, 27),
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* Writeback blocks config
|
||||
*************************************************************/
|
||||
#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \
|
||||
__xin_id, vbif_id, _reg, _wb_done_bit) \
|
||||
{ \
|
||||
.name = _name, .id = _id, \
|
||||
.base = _base, .len = 0x2c8, \
|
||||
.features = _features, \
|
||||
.format_list = wb2_formats, \
|
||||
.num_formats = ARRAY_SIZE(wb2_formats), \
|
||||
.clk_ctrl = _clk_ctrl, \
|
||||
.xin_id = __xin_id, \
|
||||
.vbif_idx = vbif_id, \
|
||||
.maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \
|
||||
.intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \
|
||||
}
|
||||
|
||||
static const struct dpu_wb_cfg sm8250_wb[] = {
|
||||
WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6,
|
||||
VBIF_RT, MDP_SSPP_TOP0_INTR, 4),
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* VBIF sub blocks config
|
||||
*************************************************************/
|
||||
@ -1643,6 +1768,8 @@ static void sdm845_cfg_init(struct dpu_mdss_cfg *dpu_cfg)
|
||||
.mixer = sdm845_lm,
|
||||
.pingpong_count = ARRAY_SIZE(sdm845_pp),
|
||||
.pingpong = sdm845_pp,
|
||||
.dsc_count = ARRAY_SIZE(sdm845_dsc),
|
||||
.dsc = sdm845_dsc,
|
||||
.intf_count = ARRAY_SIZE(sdm845_intf),
|
||||
.intf = sdm845_intf,
|
||||
.vbif_count = ARRAY_SIZE(sdm845_vbif),
|
||||
@ -1775,6 +1902,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg)
|
||||
.intf = sm8150_intf,
|
||||
.vbif_count = ARRAY_SIZE(sdm845_vbif),
|
||||
.vbif = sdm845_vbif,
|
||||
.wb_count = ARRAY_SIZE(sm8250_wb),
|
||||
.wb = sm8250_wb,
|
||||
.reg_dma_count = 1,
|
||||
.dma_cfg = sm8250_regdma,
|
||||
.perf = sm8250_perf_data,
|
||||
|
@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
|
||||
/*
|
||||
* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DPU_HW_CATALOG_H
|
||||
@ -112,6 +114,7 @@ enum {
|
||||
* @DPU_SSPP_TS_PREFILL Supports prefill with traffic shaper
|
||||
* @DPU_SSPP_TS_PREFILL_REC1 Supports prefill with traffic shaper multirec
|
||||
* @DPU_SSPP_CDP Supports client driven prefetch
|
||||
* @DPU_SSPP_INLINE_ROTATION Support inline rotation
|
||||
* @DPU_SSPP_MAX maximum value
|
||||
*/
|
||||
enum {
|
||||
@ -132,6 +135,7 @@ enum {
|
||||
DPU_SSPP_TS_PREFILL,
|
||||
DPU_SSPP_TS_PREFILL_REC1,
|
||||
DPU_SSPP_CDP,
|
||||
DPU_SSPP_INLINE_ROTATION,
|
||||
DPU_SSPP_MAX
|
||||
};
|
||||
|
||||
@ -211,6 +215,42 @@ enum {
|
||||
DPU_INTF_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* WB sub-blocks and features
|
||||
* @DPU_WB_LINE_MODE Writeback module supports line/linear mode
|
||||
* @DPU_WB_BLOCK_MODE Writeback module supports block mode read
|
||||
* @DPU_WB_CHROMA_DOWN, Writeback chroma down block,
|
||||
* @DPU_WB_DOWNSCALE, Writeback integer downscaler,
|
||||
* @DPU_WB_DITHER, Dither block
|
||||
* @DPU_WB_TRAFFIC_SHAPER, Writeback traffic shaper bloc
|
||||
* @DPU_WB_UBWC, Writeback Universal bandwidth compression
|
||||
* @DPU_WB_YUV_CONFIG Writeback supports output of YUV colorspace
|
||||
* @DPU_WB_PIPE_ALPHA Writeback supports pipe alpha
|
||||
* @DPU_WB_XY_ROI_OFFSET Writeback supports x/y-offset of out ROI in
|
||||
* the destination image
|
||||
* @DPU_WB_QOS, Writeback supports QoS control, danger/safe/creq
|
||||
* @DPU_WB_QOS_8LVL, Writeback supports 8-level QoS control
|
||||
* @DPU_WB_CDP Writeback supports client driven prefetch
|
||||
* @DPU_WB_INPUT_CTRL Writeback supports from which pp block input pixel
|
||||
* data arrives.
|
||||
* @DPU_WB_CROP CWB supports cropping
|
||||
* @DPU_WB_MAX maximum value
|
||||
*/
|
||||
enum {
|
||||
DPU_WB_LINE_MODE = 0x1,
|
||||
DPU_WB_BLOCK_MODE,
|
||||
DPU_WB_UBWC,
|
||||
DPU_WB_YUV_CONFIG,
|
||||
DPU_WB_PIPE_ALPHA,
|
||||
DPU_WB_XY_ROI_OFFSET,
|
||||
DPU_WB_QOS,
|
||||
DPU_WB_QOS_8LVL,
|
||||
DPU_WB_CDP,
|
||||
DPU_WB_INPUT_CTRL,
|
||||
DPU_WB_CROP,
|
||||
DPU_WB_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* VBIF sub-blocks and features
|
||||
* @DPU_VBIF_QOS_OTLIM VBIF supports OT Limit
|
||||
@ -314,6 +354,18 @@ struct dpu_qos_lut_tbl {
|
||||
const struct dpu_qos_lut_entry *entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_rotation_cfg - define inline rotation config
|
||||
* @rot_maxheight: max pre rotated height allowed for rotation
|
||||
* @rot_num_formats: number of elements in @rot_format_list
|
||||
* @rot_format_list: list of supported rotator formats
|
||||
*/
|
||||
struct dpu_rotation_cfg {
|
||||
u32 rot_maxheight;
|
||||
size_t rot_num_formats;
|
||||
const u32 *rot_format_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_caps - define DPU capabilities
|
||||
* @max_mixer_width max layer mixer line width support.
|
||||
@ -369,6 +421,7 @@ struct dpu_caps {
|
||||
* @num_formats: Number of supported formats
|
||||
* @virt_format_list: Pointer to list of supported formats for virtual planes
|
||||
* @virt_num_formats: Number of supported formats for virtual planes
|
||||
* @dpu_rotation_cfg: inline rotation configuration
|
||||
*/
|
||||
struct dpu_sspp_sub_blks {
|
||||
u32 creq_vblank;
|
||||
@ -390,6 +443,7 @@ struct dpu_sspp_sub_blks {
|
||||
u32 num_formats;
|
||||
const u32 *virt_format_list;
|
||||
u32 virt_num_formats;
|
||||
const struct dpu_rotation_cfg *rotation_cfg;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -444,6 +498,7 @@ enum dpu_clk_ctrl_type {
|
||||
DPU_CLK_CTRL_CURSOR1,
|
||||
DPU_CLK_CTRL_INLINE_ROT0_SSPP,
|
||||
DPU_CLK_CTRL_REG_DMA,
|
||||
DPU_CLK_CTRL_WB2,
|
||||
DPU_CLK_CTRL_MAX,
|
||||
};
|
||||
|
||||
@ -561,6 +616,16 @@ struct dpu_merge_3d_cfg {
|
||||
const struct dpu_merge_3d_sub_blks *sblk;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_dsc_cfg - information of DSC blocks
|
||||
* @id enum identifying this block
|
||||
* @base register offset of this block
|
||||
* @features bit mask identifying sub-blocks/features
|
||||
*/
|
||||
struct dpu_dsc_cfg {
|
||||
DPU_HW_BLK_INFO;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_intf_cfg - information of timing engine blocks
|
||||
* @id enum identifying this block
|
||||
@ -581,6 +646,28 @@ struct dpu_intf_cfg {
|
||||
s32 intr_vsync;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_wb_cfg - information of writeback blocks
|
||||
* @DPU_HW_BLK_INFO: refer to the description above for DPU_HW_BLK_INFO
|
||||
* @vbif_idx: vbif client index
|
||||
* @maxlinewidth: max line width supported by writeback block
|
||||
* @xin_id: bus client identifier
|
||||
* @intr_wb_done: interrupt index for WB_DONE
|
||||
* @format_list: list of formats supported by this writeback block
|
||||
* @num_formats: number of formats supported by this writeback block
|
||||
* @clk_ctrl: clock control identifier
|
||||
*/
|
||||
struct dpu_wb_cfg {
|
||||
DPU_HW_BLK_INFO;
|
||||
u8 vbif_idx;
|
||||
u32 maxlinewidth;
|
||||
u32 xin_id;
|
||||
s32 intr_wb_done;
|
||||
const u32 *format_list;
|
||||
u32 num_formats;
|
||||
enum dpu_clk_ctrl_type clk_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_vbif_dynamic_ot_cfg - dynamic OT setting
|
||||
* @pps pixel per seconds
|
||||
@ -757,12 +844,18 @@ struct dpu_mdss_cfg {
|
||||
u32 merge_3d_count;
|
||||
const struct dpu_merge_3d_cfg *merge_3d;
|
||||
|
||||
u32 dsc_count;
|
||||
struct dpu_dsc_cfg *dsc;
|
||||
|
||||
u32 intf_count;
|
||||
const struct dpu_intf_cfg *intf;
|
||||
|
||||
u32 vbif_count;
|
||||
const struct dpu_vbif_cfg *vbif;
|
||||
|
||||
u32 wb_count;
|
||||
const struct dpu_wb_cfg *wb;
|
||||
|
||||
u32 reg_dma_count;
|
||||
struct dpu_reg_dma_cfg dma_cfg;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -23,8 +24,12 @@
|
||||
#define CTL_SW_RESET 0x030
|
||||
#define CTL_LAYER_EXTN_OFFSET 0x40
|
||||
#define CTL_MERGE_3D_ACTIVE 0x0E4
|
||||
#define CTL_WB_ACTIVE 0x0EC
|
||||
#define CTL_INTF_ACTIVE 0x0F4
|
||||
#define CTL_MERGE_3D_FLUSH 0x100
|
||||
#define CTL_DSC_ACTIVE 0x0E8
|
||||
#define CTL_DSC_FLUSH 0x104
|
||||
#define CTL_WB_FLUSH 0x108
|
||||
#define CTL_INTF_FLUSH 0x110
|
||||
#define CTL_INTF_MASTER 0x134
|
||||
#define CTL_FETCH_PIPE_ACTIVE 0x0FC
|
||||
@ -34,7 +39,9 @@
|
||||
|
||||
#define DPU_REG_RESET_TIMEOUT_US 2000
|
||||
#define MERGE_3D_IDX 23
|
||||
#define DSC_IDX 22
|
||||
#define INTF_IDX 31
|
||||
#define WB_IDX 16
|
||||
#define CTL_INVALID_BIT 0xffff
|
||||
#define CTL_DEFAULT_GROUP_ID 0xf
|
||||
|
||||
@ -126,13 +133,15 @@ static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx)
|
||||
|
||||
static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
|
||||
{
|
||||
|
||||
if (ctx->pending_flush_mask & BIT(MERGE_3D_IDX))
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_MERGE_3D_FLUSH,
|
||||
ctx->pending_merge_3d_flush_mask);
|
||||
if (ctx->pending_flush_mask & BIT(INTF_IDX))
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH,
|
||||
ctx->pending_intf_flush_mask);
|
||||
if (ctx->pending_flush_mask & BIT(WB_IDX))
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
|
||||
ctx->pending_wb_flush_mask);
|
||||
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
|
||||
}
|
||||
@ -253,6 +262,27 @@ static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_update_pending_flush_wb(struct dpu_hw_ctl *ctx,
|
||||
enum dpu_wb wb)
|
||||
{
|
||||
switch (wb) {
|
||||
case WB_0:
|
||||
case WB_1:
|
||||
case WB_2:
|
||||
ctx->pending_flush_mask |= BIT(WB_IDX);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx,
|
||||
enum dpu_wb wb)
|
||||
{
|
||||
ctx->pending_wb_flush_mask |= BIT(wb - WB_0);
|
||||
ctx->pending_flush_mask |= BIT(WB_IDX);
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx,
|
||||
enum dpu_intf intf)
|
||||
{
|
||||
@ -502,6 +532,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 intf_active = 0;
|
||||
u32 wb_active = 0;
|
||||
u32 mode_sel = 0;
|
||||
|
||||
/* CTL_TOP[31:28] carries group_id to collate CTL paths
|
||||
@ -511,17 +542,32 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
|
||||
if ((test_bit(DPU_CTL_VM_CFG, &ctx->caps->features)))
|
||||
mode_sel = CTL_DEFAULT_GROUP_ID << 28;
|
||||
|
||||
if (cfg->dsc)
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_DSC_FLUSH, cfg->dsc);
|
||||
|
||||
if (cfg->intf_mode_sel == DPU_CTL_MODE_SEL_CMD)
|
||||
mode_sel |= BIT(17);
|
||||
|
||||
intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
|
||||
intf_active |= BIT(cfg->intf - INTF_0);
|
||||
wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
|
||||
|
||||
if (cfg->intf)
|
||||
intf_active |= BIT(cfg->intf - INTF_0);
|
||||
|
||||
if (cfg->wb)
|
||||
wb_active |= BIT(cfg->wb - WB_0);
|
||||
|
||||
DPU_REG_WRITE(c, CTL_TOP, mode_sel);
|
||||
DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
|
||||
DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
|
||||
|
||||
if (cfg->merge_3d)
|
||||
DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
|
||||
BIT(cfg->merge_3d - MERGE_3D_0));
|
||||
if (cfg->dsc) {
|
||||
DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, DSC_IDX);
|
||||
DPU_REG_WRITE(c, CTL_DSC_ACTIVE, cfg->dsc);
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
|
||||
@ -537,6 +583,9 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
|
||||
intf_cfg |= (cfg->mode_3d - 0x1) << 20;
|
||||
}
|
||||
|
||||
if (cfg->wb)
|
||||
intf_cfg |= (cfg->wb & 0x3) + 2;
|
||||
|
||||
switch (cfg->intf_mode_sel) {
|
||||
case DPU_CTL_MODE_SEL_VID:
|
||||
intf_cfg &= ~BIT(17);
|
||||
@ -554,6 +603,44 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
|
||||
DPU_REG_WRITE(c, CTL_TOP, intf_cfg);
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
|
||||
struct dpu_hw_intf_cfg *cfg)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 intf_active = 0;
|
||||
u32 wb_active = 0;
|
||||
u32 merge3d_active = 0;
|
||||
|
||||
/*
|
||||
* This API resets each portion of the CTL path namely,
|
||||
* clearing the sspps staged on the lm, merge_3d block,
|
||||
* interfaces , writeback etc to ensure clean teardown of the pipeline.
|
||||
* This will be used for writeback to begin with to have a
|
||||
* proper teardown of the writeback session but upon further
|
||||
* validation, this can be extended to all interfaces.
|
||||
*/
|
||||
if (cfg->merge_3d) {
|
||||
merge3d_active = DPU_REG_READ(c, CTL_MERGE_3D_ACTIVE);
|
||||
merge3d_active &= ~BIT(cfg->merge_3d - MERGE_3D_0);
|
||||
DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
|
||||
merge3d_active);
|
||||
}
|
||||
|
||||
dpu_hw_ctl_clear_all_blendstages(ctx);
|
||||
|
||||
if (cfg->intf) {
|
||||
intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
|
||||
intf_active &= ~BIT(cfg->intf - INTF_0);
|
||||
DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
|
||||
}
|
||||
|
||||
if (cfg->wb) {
|
||||
wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
|
||||
wb_active &= ~BIT(cfg->wb - WB_0);
|
||||
DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
|
||||
unsigned long *fetch_active)
|
||||
{
|
||||
@ -577,15 +664,18 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
|
||||
if (cap & BIT(DPU_CTL_ACTIVE_CFG)) {
|
||||
ops->trigger_flush = dpu_hw_ctl_trigger_flush_v1;
|
||||
ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg_v1;
|
||||
ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
|
||||
ops->update_pending_flush_intf =
|
||||
dpu_hw_ctl_update_pending_flush_intf_v1;
|
||||
ops->update_pending_flush_merge_3d =
|
||||
dpu_hw_ctl_update_pending_flush_merge_3d_v1;
|
||||
ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1;
|
||||
} else {
|
||||
ops->trigger_flush = dpu_hw_ctl_trigger_flush;
|
||||
ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
|
||||
ops->update_pending_flush_intf =
|
||||
dpu_hw_ctl_update_pending_flush_intf;
|
||||
ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb;
|
||||
}
|
||||
ops->clear_pending_flush = dpu_hw_ctl_clear_pending_flush;
|
||||
ops->update_pending_flush = dpu_hw_ctl_update_pending_flush;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DPU_HW_CTL_H
|
||||
@ -40,13 +41,16 @@ struct dpu_hw_stage_cfg {
|
||||
* @merge_3d: 3d merge block used
|
||||
* @intf_mode_sel: Interface mode, cmd / vid
|
||||
* @stream_sel: Stream selection for multi-stream interfaces
|
||||
* @dsc: DSC BIT masks used
|
||||
*/
|
||||
struct dpu_hw_intf_cfg {
|
||||
enum dpu_intf intf;
|
||||
enum dpu_wb wb;
|
||||
enum dpu_3d_blend_mode mode_3d;
|
||||
enum dpu_merge_3d merge_3d;
|
||||
enum dpu_ctl_mode_sel intf_mode_sel;
|
||||
int stream_sel;
|
||||
unsigned int dsc;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -99,6 +103,15 @@ struct dpu_hw_ctl_ops {
|
||||
void (*update_pending_flush)(struct dpu_hw_ctl *ctx,
|
||||
u32 flushbits);
|
||||
|
||||
/**
|
||||
* OR in the given flushbits to the cached pending_(wb_)flush_mask
|
||||
* No effect on hardware
|
||||
* @ctx : ctl path ctx pointer
|
||||
* @blk : writeback block index
|
||||
*/
|
||||
void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx,
|
||||
enum dpu_wb blk);
|
||||
|
||||
/**
|
||||
* OR in the given flushbits to the cached pending_(intf_)flush_mask
|
||||
* No effect on hardware
|
||||
@ -138,6 +151,14 @@ struct dpu_hw_ctl_ops {
|
||||
void (*setup_intf_cfg)(struct dpu_hw_ctl *ctx,
|
||||
struct dpu_hw_intf_cfg *cfg);
|
||||
|
||||
/**
|
||||
* reset ctl_path interface config
|
||||
* @ctx : ctl path ctx pointer
|
||||
* @cfg : interface config structure pointer
|
||||
*/
|
||||
void (*reset_intf_cfg)(struct dpu_hw_ctl *ctx,
|
||||
struct dpu_hw_intf_cfg *cfg);
|
||||
|
||||
int (*reset)(struct dpu_hw_ctl *c);
|
||||
|
||||
/*
|
||||
@ -189,6 +210,7 @@ struct dpu_hw_ctl_ops {
|
||||
* @mixer_hw_caps: mixer hardware capabilities
|
||||
* @pending_flush_mask: storage for pending ctl_flush managed via ops
|
||||
* @pending_intf_flush_mask: pending INTF flush
|
||||
* @pending_wb_flush_mask: pending WB flush
|
||||
* @ops: operation list
|
||||
*/
|
||||
struct dpu_hw_ctl {
|
||||
@ -202,6 +224,7 @@ struct dpu_hw_ctl {
|
||||
const struct dpu_lm_cfg *mixer_hw_caps;
|
||||
u32 pending_flush_mask;
|
||||
u32 pending_intf_flush_mask;
|
||||
u32 pending_wb_flush_mask;
|
||||
u32 pending_merge_3d_flush_mask;
|
||||
|
||||
/* ops */
|
||||
|
215
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
Normal file
215
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c
Normal file
@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2022, Linaro Limited
|
||||
*/
|
||||
|
||||
#include "dpu_kms.h"
|
||||
#include "dpu_hw_catalog.h"
|
||||
#include "dpu_hwio.h"
|
||||
#include "dpu_hw_mdss.h"
|
||||
#include "dpu_hw_dsc.h"
|
||||
|
||||
#define DSC_COMMON_MODE 0x000
|
||||
#define DSC_ENC 0x004
|
||||
#define DSC_PICTURE 0x008
|
||||
#define DSC_SLICE 0x00C
|
||||
#define DSC_CHUNK_SIZE 0x010
|
||||
#define DSC_DELAY 0x014
|
||||
#define DSC_SCALE_INITIAL 0x018
|
||||
#define DSC_SCALE_DEC_INTERVAL 0x01C
|
||||
#define DSC_SCALE_INC_INTERVAL 0x020
|
||||
#define DSC_FIRST_LINE_BPG_OFFSET 0x024
|
||||
#define DSC_BPG_OFFSET 0x028
|
||||
#define DSC_DSC_OFFSET 0x02C
|
||||
#define DSC_FLATNESS 0x030
|
||||
#define DSC_RC_MODEL_SIZE 0x034
|
||||
#define DSC_RC 0x038
|
||||
#define DSC_RC_BUF_THRESH 0x03C
|
||||
#define DSC_RANGE_MIN_QP 0x074
|
||||
#define DSC_RANGE_MAX_QP 0x0B0
|
||||
#define DSC_RANGE_BPG_OFFSET 0x0EC
|
||||
|
||||
static void dpu_hw_dsc_disable(struct dpu_hw_dsc *dsc)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &dsc->hw;
|
||||
|
||||
DPU_REG_WRITE(c, DSC_COMMON_MODE, 0);
|
||||
}
|
||||
|
||||
static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc,
|
||||
struct msm_display_dsc_config *dsc,
|
||||
u32 mode,
|
||||
u32 initial_lines)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
|
||||
u32 data, lsb, bpp;
|
||||
u32 slice_last_group_size;
|
||||
u32 det_thresh_flatness;
|
||||
bool is_cmd_mode = !(mode & DSC_MODE_VIDEO);
|
||||
|
||||
DPU_REG_WRITE(c, DSC_COMMON_MODE, mode);
|
||||
|
||||
if (is_cmd_mode)
|
||||
initial_lines += 1;
|
||||
|
||||
slice_last_group_size = 3 - (dsc->drm->slice_width % 3);
|
||||
data = (initial_lines << 20);
|
||||
data |= ((slice_last_group_size - 1) << 18);
|
||||
/* bpp is 6.4 format, 4 LSBs bits are for fractional part */
|
||||
data |= dsc->drm->bits_per_pixel << 12;
|
||||
lsb = dsc->drm->bits_per_pixel % 4;
|
||||
bpp = dsc->drm->bits_per_pixel / 4;
|
||||
bpp *= 4;
|
||||
bpp <<= 4;
|
||||
bpp |= lsb;
|
||||
|
||||
data |= bpp << 8;
|
||||
data |= (dsc->drm->block_pred_enable << 7);
|
||||
data |= (dsc->drm->line_buf_depth << 3);
|
||||
data |= (dsc->drm->simple_422 << 2);
|
||||
data |= (dsc->drm->convert_rgb << 1);
|
||||
data |= dsc->drm->bits_per_component;
|
||||
|
||||
DPU_REG_WRITE(c, DSC_ENC, data);
|
||||
|
||||
data = dsc->drm->pic_width << 16;
|
||||
data |= dsc->drm->pic_height;
|
||||
DPU_REG_WRITE(c, DSC_PICTURE, data);
|
||||
|
||||
data = dsc->drm->slice_width << 16;
|
||||
data |= dsc->drm->slice_height;
|
||||
DPU_REG_WRITE(c, DSC_SLICE, data);
|
||||
|
||||
data = dsc->drm->slice_chunk_size << 16;
|
||||
DPU_REG_WRITE(c, DSC_CHUNK_SIZE, data);
|
||||
|
||||
data = dsc->drm->initial_dec_delay << 16;
|
||||
data |= dsc->drm->initial_xmit_delay;
|
||||
DPU_REG_WRITE(c, DSC_DELAY, data);
|
||||
|
||||
data = dsc->drm->initial_scale_value;
|
||||
DPU_REG_WRITE(c, DSC_SCALE_INITIAL, data);
|
||||
|
||||
data = dsc->drm->scale_decrement_interval;
|
||||
DPU_REG_WRITE(c, DSC_SCALE_DEC_INTERVAL, data);
|
||||
|
||||
data = dsc->drm->scale_increment_interval;
|
||||
DPU_REG_WRITE(c, DSC_SCALE_INC_INTERVAL, data);
|
||||
|
||||
data = dsc->drm->first_line_bpg_offset;
|
||||
DPU_REG_WRITE(c, DSC_FIRST_LINE_BPG_OFFSET, data);
|
||||
|
||||
data = dsc->drm->nfl_bpg_offset << 16;
|
||||
data |= dsc->drm->slice_bpg_offset;
|
||||
DPU_REG_WRITE(c, DSC_BPG_OFFSET, data);
|
||||
|
||||
data = dsc->drm->initial_offset << 16;
|
||||
data |= dsc->drm->final_offset;
|
||||
DPU_REG_WRITE(c, DSC_DSC_OFFSET, data);
|
||||
|
||||
det_thresh_flatness = 7 + 2 * (dsc->drm->bits_per_component - 8);
|
||||
data = det_thresh_flatness << 10;
|
||||
data |= dsc->drm->flatness_max_qp << 5;
|
||||
data |= dsc->drm->flatness_min_qp;
|
||||
DPU_REG_WRITE(c, DSC_FLATNESS, data);
|
||||
|
||||
data = dsc->drm->rc_model_size;
|
||||
DPU_REG_WRITE(c, DSC_RC_MODEL_SIZE, data);
|
||||
|
||||
data = dsc->drm->rc_tgt_offset_low << 18;
|
||||
data |= dsc->drm->rc_tgt_offset_high << 14;
|
||||
data |= dsc->drm->rc_quant_incr_limit1 << 9;
|
||||
data |= dsc->drm->rc_quant_incr_limit0 << 4;
|
||||
data |= dsc->drm->rc_edge_factor;
|
||||
DPU_REG_WRITE(c, DSC_RC, data);
|
||||
}
|
||||
|
||||
static void dpu_hw_dsc_config_thresh(struct dpu_hw_dsc *hw_dsc,
|
||||
struct msm_display_dsc_config *dsc)
|
||||
{
|
||||
struct drm_dsc_rc_range_parameters *rc = dsc->drm->rc_range_params;
|
||||
struct dpu_hw_blk_reg_map *c = &hw_dsc->hw;
|
||||
u32 off;
|
||||
int i;
|
||||
|
||||
off = DSC_RC_BUF_THRESH;
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES - 1 ; i++) {
|
||||
DPU_REG_WRITE(c, off, dsc->drm->rc_buf_thresh[i]);
|
||||
off += 4;
|
||||
}
|
||||
|
||||
off = DSC_RANGE_MIN_QP;
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
|
||||
DPU_REG_WRITE(c, off, rc[i].range_min_qp);
|
||||
off += 4;
|
||||
}
|
||||
|
||||
off = DSC_RANGE_MAX_QP;
|
||||
for (i = 0; i < 15; i++) {
|
||||
DPU_REG_WRITE(c, off, rc[i].range_max_qp);
|
||||
off += 4;
|
||||
}
|
||||
|
||||
off = DSC_RANGE_BPG_OFFSET;
|
||||
for (i = 0; i < 15; i++) {
|
||||
DPU_REG_WRITE(c, off, rc[i].range_bpg_offset);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static struct dpu_dsc_cfg *_dsc_offset(enum dpu_dsc dsc,
|
||||
struct dpu_mdss_cfg *m,
|
||||
void __iomem *addr,
|
||||
struct dpu_hw_blk_reg_map *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < m->dsc_count; i++) {
|
||||
if (dsc == m->dsc[i].id) {
|
||||
b->base_off = addr;
|
||||
b->blk_off = m->dsc[i].base;
|
||||
b->length = m->dsc[i].len;
|
||||
b->hwversion = m->hwversion;
|
||||
b->log_mask = DPU_DBG_MASK_DSC;
|
||||
return &m->dsc[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _setup_dsc_ops(struct dpu_hw_dsc_ops *ops,
|
||||
unsigned long cap)
|
||||
{
|
||||
ops->dsc_disable = dpu_hw_dsc_disable;
|
||||
ops->dsc_config = dpu_hw_dsc_config;
|
||||
ops->dsc_config_thresh = dpu_hw_dsc_config_thresh;
|
||||
};
|
||||
|
||||
struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
|
||||
struct dpu_mdss_cfg *m)
|
||||
{
|
||||
struct dpu_hw_dsc *c;
|
||||
struct dpu_dsc_cfg *cfg;
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cfg = _dsc_offset(idx, m, addr, &c->hw);
|
||||
if (IS_ERR_OR_NULL(cfg)) {
|
||||
kfree(c);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
c->idx = idx;
|
||||
c->caps = cfg;
|
||||
_setup_dsc_ops(&c->ops, c->caps->features);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc)
|
||||
{
|
||||
kfree(dsc);
|
||||
}
|
80
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
Normal file
80
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
Normal file
@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2020-2022, Linaro Limited */
|
||||
|
||||
#ifndef _DPU_HW_DSC_H
|
||||
#define _DPU_HW_DSC_H
|
||||
|
||||
#include <drm/display/drm_dsc.h>
|
||||
|
||||
#define DSC_MODE_SPLIT_PANEL BIT(0)
|
||||
#define DSC_MODE_MULTIPLEX BIT(1)
|
||||
#define DSC_MODE_VIDEO BIT(2)
|
||||
|
||||
struct dpu_hw_dsc;
|
||||
|
||||
/**
|
||||
* struct dpu_hw_dsc_ops - interface to the dsc hardware driver functions
|
||||
* Assumption is these functions will be called after clocks are enabled
|
||||
*/
|
||||
struct dpu_hw_dsc_ops {
|
||||
/**
|
||||
* dsc_disable - disable dsc
|
||||
* @hw_dsc: Pointer to dsc context
|
||||
*/
|
||||
void (*dsc_disable)(struct dpu_hw_dsc *hw_dsc);
|
||||
|
||||
/**
|
||||
* dsc_config - configures dsc encoder
|
||||
* @hw_dsc: Pointer to dsc context
|
||||
* @dsc: panel dsc parameters
|
||||
* @mode: dsc topology mode to be set
|
||||
* @initial_lines: amount of initial lines to be used
|
||||
*/
|
||||
void (*dsc_config)(struct dpu_hw_dsc *hw_dsc,
|
||||
struct msm_display_dsc_config *dsc,
|
||||
u32 mode,
|
||||
u32 initial_lines);
|
||||
|
||||
/**
|
||||
* dsc_config_thresh - programs panel thresholds
|
||||
* @hw_dsc: Pointer to dsc context
|
||||
* @dsc: panel dsc parameters
|
||||
*/
|
||||
void (*dsc_config_thresh)(struct dpu_hw_dsc *hw_dsc,
|
||||
struct msm_display_dsc_config *dsc);
|
||||
};
|
||||
|
||||
struct dpu_hw_dsc {
|
||||
struct dpu_hw_blk base;
|
||||
struct dpu_hw_blk_reg_map hw;
|
||||
|
||||
/* dsc */
|
||||
enum dpu_dsc idx;
|
||||
const struct dpu_dsc_cfg *caps;
|
||||
|
||||
/* ops */
|
||||
struct dpu_hw_dsc_ops ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* dpu_hw_dsc_init - initializes the dsc block for the passed dsc idx.
|
||||
* @idx: DSC index for which driver object is required
|
||||
* @addr: Mapped register io address of MDP
|
||||
* @m: Pointer to mdss catalog data
|
||||
* Returns: Error code or allocated dpu_hw_dsc context
|
||||
*/
|
||||
struct dpu_hw_dsc *dpu_hw_dsc_init(enum dpu_dsc idx, void __iomem *addr,
|
||||
struct dpu_mdss_cfg *m);
|
||||
|
||||
/**
|
||||
* dpu_hw_dsc_destroy - destroys dsc driver context
|
||||
* @dsc: Pointer to dsc driver context returned by dpu_hw_dsc_init
|
||||
*/
|
||||
void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
|
||||
|
||||
static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
|
||||
{
|
||||
return container_of(hw, struct dpu_hw_dsc, base);
|
||||
}
|
||||
|
||||
#endif /* _DPU_HW_DSC_H */
|
@ -151,25 +151,22 @@ static const struct dpu_intr_reg dpu_intr_set[] = {
|
||||
*/
|
||||
static void dpu_core_irq_callback_handler(struct dpu_kms *dpu_kms, int irq_idx)
|
||||
{
|
||||
struct dpu_irq_callback *cb;
|
||||
|
||||
VERB("irq_idx=%d\n", irq_idx);
|
||||
|
||||
if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]))
|
||||
if (!dpu_kms->hw_intr->irq_tbl[irq_idx].cb)
|
||||
DRM_ERROR("no registered cb, idx:%d\n", irq_idx);
|
||||
|
||||
atomic_inc(&dpu_kms->hw_intr->irq_counts[irq_idx]);
|
||||
atomic_inc(&dpu_kms->hw_intr->irq_tbl[irq_idx].count);
|
||||
|
||||
/*
|
||||
* Perform registered function callback
|
||||
*/
|
||||
list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[irq_idx], list)
|
||||
if (cb->func)
|
||||
cb->func(cb->arg, irq_idx);
|
||||
dpu_kms->hw_intr->irq_tbl[irq_idx].cb(dpu_kms->hw_intr->irq_tbl[irq_idx].arg, irq_idx);
|
||||
}
|
||||
|
||||
irqreturn_t dpu_core_irq(struct dpu_kms *dpu_kms)
|
||||
irqreturn_t dpu_core_irq(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int reg_idx;
|
||||
int irq_idx;
|
||||
@ -362,7 +359,7 @@ static void dpu_disable_all_irqs(struct dpu_kms *dpu_kms)
|
||||
wmb();
|
||||
}
|
||||
|
||||
u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
|
||||
u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx)
|
||||
{
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int reg_idx;
|
||||
@ -389,7 +386,7 @@ u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
|
||||
intr_status = DPU_REG_READ(&intr->hw,
|
||||
dpu_intr_set[reg_idx].status_off) &
|
||||
DPU_IRQ_MASK(irq_idx);
|
||||
if (intr_status && clear)
|
||||
if (intr_status)
|
||||
DPU_REG_WRITE(&intr->hw, dpu_intr_set[reg_idx].clr_off,
|
||||
intr_status);
|
||||
|
||||
@ -413,24 +410,18 @@ struct dpu_hw_intr *dpu_hw_intr_init(void __iomem *addr,
|
||||
struct dpu_mdss_cfg *m)
|
||||
{
|
||||
struct dpu_hw_intr *intr;
|
||||
int nirq = MDP_INTR_MAX * 32;
|
||||
|
||||
if (!addr || !m)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
intr = kzalloc(sizeof(*intr), GFP_KERNEL);
|
||||
intr = kzalloc(struct_size(intr, irq_tbl, nirq), GFP_KERNEL);
|
||||
if (!intr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
__intr_offset(m, addr, &intr->hw);
|
||||
|
||||
intr->total_irqs = ARRAY_SIZE(dpu_intr_set) * 32;
|
||||
|
||||
intr->cache_irq_mask = kcalloc(ARRAY_SIZE(dpu_intr_set), sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
if (intr->cache_irq_mask == NULL) {
|
||||
kfree(intr);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
intr->total_irqs = nirq;
|
||||
|
||||
intr->irq_mask = m->mdss_irqs;
|
||||
|
||||
@ -441,31 +432,18 @@ struct dpu_hw_intr *dpu_hw_intr_init(void __iomem *addr,
|
||||
|
||||
void dpu_hw_intr_destroy(struct dpu_hw_intr *intr)
|
||||
{
|
||||
if (intr) {
|
||||
kfree(intr->cache_irq_mask);
|
||||
|
||||
kfree(intr->irq_cb_tbl);
|
||||
kfree(intr->irq_counts);
|
||||
|
||||
kfree(intr);
|
||||
}
|
||||
kfree(intr);
|
||||
}
|
||||
|
||||
int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
void (*irq_cb)(void *arg, int irq_idx),
|
||||
void *irq_arg)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
int ret;
|
||||
|
||||
if (!dpu_kms->hw_intr->irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
if (!irq_cb) {
|
||||
DPU_ERROR("invalid ird_idx:%d irq_cb:%ps\n", irq_idx, irq_cb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -477,41 +455,34 @@ int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
trace_dpu_core_irq_register_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
list_add_tail(®ister_irq_cb->list,
|
||||
&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]);
|
||||
if (list_is_first(®ister_irq_cb->list,
|
||||
&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_hw_intr_enable_irq_locked(
|
||||
|
||||
if (unlikely(WARN_ON(dpu_kms->hw_intr->irq_tbl[irq_idx].cb))) {
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
trace_dpu_core_irq_register_callback(irq_idx, irq_cb);
|
||||
dpu_kms->hw_intr->irq_tbl[irq_idx].arg = irq_arg;
|
||||
dpu_kms->hw_intr->irq_tbl[irq_idx].cb = irq_cb;
|
||||
|
||||
ret = dpu_hw_intr_enable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to enable IRQ for irq_idx:%d\n",
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to enable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
}
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
trace_dpu_irq_register_success(irq_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!dpu_kms->hw_intr->irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
int ret;
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
|
||||
DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
|
||||
@ -521,20 +492,20 @@ int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
trace_dpu_core_irq_unregister_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
/* empty callback list but interrupt is still enabled */
|
||||
if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_hw_intr_disable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to disable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
VERB("irq_idx=%d ret=%d\n", irq_idx, ret);
|
||||
}
|
||||
trace_dpu_core_irq_unregister_callback(irq_idx);
|
||||
|
||||
ret = dpu_hw_intr_disable_irq_locked(dpu_kms->hw_intr, irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to disable IRQ for irq_idx:%d: %d\n",
|
||||
irq_idx, ret);
|
||||
|
||||
dpu_kms->hw_intr->irq_tbl[irq_idx].cb = NULL;
|
||||
dpu_kms->hw_intr->irq_tbl[irq_idx].arg = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
trace_dpu_irq_unregister_success(irq_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -542,24 +513,18 @@ int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
static int dpu_debugfs_core_irq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = s->private;
|
||||
struct dpu_irq_callback *cb;
|
||||
unsigned long irq_flags;
|
||||
int i, irq_count, cb_count;
|
||||
|
||||
if (WARN_ON(!dpu_kms->hw_intr->irq_cb_tbl))
|
||||
return 0;
|
||||
int i, irq_count;
|
||||
void *cb;
|
||||
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
cb_count = 0;
|
||||
irq_count = atomic_read(&dpu_kms->hw_intr->irq_counts[i]);
|
||||
list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[i], list)
|
||||
cb_count++;
|
||||
irq_count = atomic_read(&dpu_kms->hw_intr->irq_tbl[i].count);
|
||||
cb = dpu_kms->hw_intr->irq_tbl[i].cb;
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
if (irq_count || cb_count)
|
||||
seq_printf(s, "idx:%d irq:%d cb:%d\n",
|
||||
i, irq_count, cb_count);
|
||||
if (irq_count || cb)
|
||||
seq_printf(s, "idx:%d irq:%d cb:%ps\n", i, irq_count, cb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -575,8 +540,9 @@ void dpu_debugfs_core_irq_init(struct dpu_kms *dpu_kms,
|
||||
}
|
||||
#endif
|
||||
|
||||
void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
|
||||
void dpu_core_irq_preinstall(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
@ -584,24 +550,21 @@ void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
|
||||
dpu_disable_all_irqs(dpu_kms);
|
||||
pm_runtime_put_sync(&dpu_kms->pdev->dev);
|
||||
|
||||
/* Create irq callbacks for all possible irq_idx */
|
||||
dpu_kms->hw_intr->irq_cb_tbl = kcalloc(dpu_kms->hw_intr->total_irqs,
|
||||
sizeof(struct list_head), GFP_KERNEL);
|
||||
dpu_kms->hw_intr->irq_counts = kcalloc(dpu_kms->hw_intr->total_irqs,
|
||||
sizeof(atomic_t), GFP_KERNEL);
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
|
||||
INIT_LIST_HEAD(&dpu_kms->hw_intr->irq_cb_tbl[i]);
|
||||
atomic_set(&dpu_kms->hw_intr->irq_counts[i], 0);
|
||||
}
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++)
|
||||
atomic_set(&dpu_kms->hw_intr->irq_tbl[i].count, 0);
|
||||
}
|
||||
|
||||
void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms)
|
||||
void dpu_core_irq_uninstall(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
int i;
|
||||
|
||||
if (!dpu_kms->hw_intr)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++)
|
||||
if (!list_empty(&dpu_kms->hw_intr->irq_cb_tbl[i]))
|
||||
if (dpu_kms->hw_intr->irq_tbl[i].cb)
|
||||
DPU_ERROR("irq_idx=%d still enabled/registered\n", i);
|
||||
|
||||
dpu_clear_irqs(dpu_kms);
|
||||
|
@ -44,19 +44,21 @@ enum dpu_hw_intr_reg {
|
||||
* @save_irq_status: array of IRQ status reg storage created during init
|
||||
* @total_irqs: total number of irq_idx mapped in the hw_interrupts
|
||||
* @irq_lock: spinlock for accessing IRQ resources
|
||||
* @irq_cb_tbl: array of IRQ callbacks lists
|
||||
* @irq_counts: array of IRQ counts
|
||||
* @irq_cb_tbl: array of IRQ callbacks
|
||||
*/
|
||||
struct dpu_hw_intr {
|
||||
struct dpu_hw_blk_reg_map hw;
|
||||
u32 *cache_irq_mask;
|
||||
u32 cache_irq_mask[MDP_INTR_MAX];
|
||||
u32 *save_irq_status;
|
||||
u32 total_irqs;
|
||||
spinlock_t irq_lock;
|
||||
unsigned long irq_mask;
|
||||
|
||||
struct list_head *irq_cb_tbl;
|
||||
atomic_t *irq_counts;
|
||||
struct {
|
||||
void (*cb)(void *arg, int irq_idx);
|
||||
void *arg;
|
||||
atomic_t count;
|
||||
} irq_tbl[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,7 @@
|
||||
#define INTF_TP_COLOR1 0x05C
|
||||
#define INTF_CONFIG2 0x060
|
||||
#define INTF_DISPLAY_DATA_HCTL 0x064
|
||||
#define INTF_ACTIVE_DATA_HCTL 0x068
|
||||
#define INTF_FRAME_LINE_COUNT_EN 0x0A8
|
||||
#define INTF_FRAME_COUNT 0x0AC
|
||||
#define INTF_LINE_COUNT 0x0B0
|
||||
@ -60,6 +61,12 @@
|
||||
|
||||
#define INTF_MUX 0x25C
|
||||
|
||||
#define INTF_CFG_ACTIVE_H_EN BIT(29)
|
||||
#define INTF_CFG_ACTIVE_V_EN BIT(30)
|
||||
|
||||
#define INTF_CFG2_DATABUS_WIDEN BIT(0)
|
||||
#define INTF_CFG2_DATA_HCTL_EN BIT(4)
|
||||
|
||||
static const struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf,
|
||||
const struct dpu_mdss_cfg *m,
|
||||
void __iomem *addr,
|
||||
@ -90,15 +97,23 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
u32 hsync_period, vsync_period;
|
||||
u32 display_v_start, display_v_end;
|
||||
u32 hsync_start_x, hsync_end_x;
|
||||
u32 hsync_data_start_x, hsync_data_end_x;
|
||||
u32 active_h_start, active_h_end;
|
||||
u32 active_v_start, active_v_end;
|
||||
u32 active_hctl, display_hctl, hsync_ctl;
|
||||
u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity;
|
||||
u32 panel_format;
|
||||
u32 intf_cfg, intf_cfg2 = 0, display_data_hctl = 0;
|
||||
u32 intf_cfg, intf_cfg2 = 0;
|
||||
u32 display_data_hctl = 0, active_data_hctl = 0;
|
||||
u32 data_width;
|
||||
bool dp_intf = false;
|
||||
|
||||
/* read interface_cfg */
|
||||
intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
|
||||
|
||||
if (ctx->cap->type == INTF_DP)
|
||||
dp_intf = true;
|
||||
|
||||
hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
|
||||
p->h_front_porch;
|
||||
vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height +
|
||||
@ -112,7 +127,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
|
||||
hsync_end_x = hsync_period - p->h_front_porch - 1;
|
||||
|
||||
if (p->width != p->xres) {
|
||||
if (p->width != p->xres) { /* border fill added */
|
||||
active_h_start = hsync_start_x;
|
||||
active_h_end = active_h_start + p->xres - 1;
|
||||
} else {
|
||||
@ -120,7 +135,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
active_h_end = 0;
|
||||
}
|
||||
|
||||
if (p->height != p->yres) {
|
||||
if (p->height != p->yres) { /* border fill added */
|
||||
active_v_start = display_v_start;
|
||||
active_v_end = active_v_start + (p->yres * hsync_period) - 1;
|
||||
} else {
|
||||
@ -130,27 +145,46 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
|
||||
if (active_h_end) {
|
||||
active_hctl = (active_h_end << 16) | active_h_start;
|
||||
intf_cfg |= BIT(29); /* ACTIVE_H_ENABLE */
|
||||
intf_cfg |= INTF_CFG_ACTIVE_H_EN;
|
||||
} else {
|
||||
active_hctl = 0;
|
||||
}
|
||||
|
||||
if (active_v_end)
|
||||
intf_cfg |= BIT(30); /* ACTIVE_V_ENABLE */
|
||||
intf_cfg |= INTF_CFG_ACTIVE_V_EN;
|
||||
|
||||
hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
|
||||
display_hctl = (hsync_end_x << 16) | hsync_start_x;
|
||||
|
||||
if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP) {
|
||||
/*
|
||||
* DATA_HCTL_EN controls data timing which can be different from
|
||||
* video timing. It is recommended to enable it for all cases, except
|
||||
* if compression is enabled in 1 pixel per clock mode
|
||||
*/
|
||||
if (p->wide_bus_en)
|
||||
intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN | INTF_CFG2_DATA_HCTL_EN;
|
||||
|
||||
data_width = p->width;
|
||||
|
||||
hsync_data_start_x = hsync_start_x;
|
||||
hsync_data_end_x = hsync_start_x + data_width - 1;
|
||||
|
||||
display_data_hctl = (hsync_data_end_x << 16) | hsync_data_start_x;
|
||||
|
||||
if (dp_intf) {
|
||||
/* DP timing adjustment */
|
||||
display_v_start += p->hsync_pulse_width + p->h_back_porch;
|
||||
display_v_end -= p->h_front_porch;
|
||||
|
||||
active_h_start = hsync_start_x;
|
||||
active_h_end = active_h_start + p->xres - 1;
|
||||
active_v_start = display_v_start;
|
||||
active_v_end = active_v_start + (p->yres * hsync_period) - 1;
|
||||
|
||||
display_v_start += p->hsync_pulse_width + p->h_back_porch;
|
||||
|
||||
active_hctl = (active_h_end << 16) | active_h_start;
|
||||
display_hctl = active_hctl;
|
||||
|
||||
intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN;
|
||||
}
|
||||
|
||||
den_polarity = 0;
|
||||
@ -180,13 +214,6 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
(COLOR_8BIT << 4) |
|
||||
(0x21 << 8));
|
||||
|
||||
if (ctx->cap->features & BIT(DPU_DATA_HCTL_EN)) {
|
||||
intf_cfg2 |= BIT(4);
|
||||
display_data_hctl = display_hctl;
|
||||
DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
|
||||
DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
|
||||
}
|
||||
|
||||
DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl);
|
||||
DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period);
|
||||
DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0,
|
||||
@ -204,6 +231,11 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
|
||||
DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3);
|
||||
DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg);
|
||||
DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format);
|
||||
if (ctx->cap->features & BIT(DPU_DATA_HCTL_EN)) {
|
||||
DPU_REG_WRITE(c, INTF_CONFIG2, intf_cfg2);
|
||||
DPU_REG_WRITE(c, INTF_DISPLAY_DATA_HCTL, display_data_hctl);
|
||||
DPU_REG_WRITE(c, INTF_ACTIVE_DATA_HCTL, active_data_hctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_intf_enable_timing_engine(
|
||||
|
@ -30,6 +30,8 @@ struct intf_timing_params {
|
||||
u32 border_clr;
|
||||
u32 underflow_clr;
|
||||
u32 hsync_skew;
|
||||
|
||||
bool wide_bus_en;
|
||||
};
|
||||
|
||||
struct intf_prog_fetch {
|
||||
|
@ -138,7 +138,7 @@ static int dpu_hw_lm_collect_misr(struct dpu_hw_mixer *ctx, u32 *misr_value)
|
||||
ctrl = DPU_REG_READ(c, LM_MISR_CTRL);
|
||||
|
||||
if (!(ctrl & LM_MISR_CTRL_ENABLE))
|
||||
return -EINVAL;
|
||||
return -ENODATA;
|
||||
|
||||
if (!(ctrl & LM_MISR_CTRL_STATUS))
|
||||
return -EINVAL;
|
||||
|
@ -97,6 +97,7 @@ enum dpu_hw_blk_type {
|
||||
DPU_HW_BLK_WB,
|
||||
DPU_HW_BLK_DSPP,
|
||||
DPU_HW_BLK_MERGE_3D,
|
||||
DPU_HW_BLK_DSC,
|
||||
DPU_HW_BLK_MAX,
|
||||
};
|
||||
|
||||
@ -176,6 +177,17 @@ enum dpu_ctl {
|
||||
CTL_MAX
|
||||
};
|
||||
|
||||
enum dpu_dsc {
|
||||
DSC_NONE = 0,
|
||||
DSC_0,
|
||||
DSC_1,
|
||||
DSC_2,
|
||||
DSC_3,
|
||||
DSC_4,
|
||||
DSC_5,
|
||||
DSC_MAX
|
||||
};
|
||||
|
||||
enum dpu_pingpong {
|
||||
PINGPONG_0 = 1,
|
||||
PINGPONG_1,
|
||||
@ -205,14 +217,21 @@ enum dpu_intf {
|
||||
INTF_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* Historically these values correspond to the values written to the
|
||||
* DISP_INTF_SEL register, which had to programmed manually. On newer MDP
|
||||
* generations this register is NOP, but we keep the values for historical
|
||||
* reasons.
|
||||
*/
|
||||
enum dpu_intf_type {
|
||||
INTF_NONE = 0x0,
|
||||
INTF_DSI = 0x1,
|
||||
INTF_HDMI = 0x3,
|
||||
INTF_LCDC = 0x5,
|
||||
/* old eDP found on 8x74 and 8x84 */
|
||||
INTF_EDP = 0x9,
|
||||
/* both DP and eDP, handled by the new DP driver */
|
||||
INTF_DP = 0xa,
|
||||
INTF_TYPE_MAX,
|
||||
|
||||
/* virtual interfaces */
|
||||
INTF_WB = 0x100,
|
||||
@ -437,5 +456,6 @@ struct dpu_mdss_color {
|
||||
#define DPU_DBG_MASK_VBIF (1 << 8)
|
||||
#define DPU_DBG_MASK_ROT (1 << 9)
|
||||
#define DPU_DBG_MASK_DSPP (1 << 10)
|
||||
#define DPU_DBG_MASK_DSC (1 << 11)
|
||||
|
||||
#endif /* _DPU_HW_MDSS_H */
|
||||
|
@ -28,6 +28,9 @@
|
||||
#define PP_FBC_MODE 0x034
|
||||
#define PP_FBC_BUDGET_CTL 0x038
|
||||
#define PP_FBC_LOSSY_MODE 0x03C
|
||||
#define PP_DSC_MODE 0x0a0
|
||||
#define PP_DCE_DATA_IN_SWAP 0x0ac
|
||||
#define PP_DCE_DATA_OUT_SWAP 0x0c8
|
||||
|
||||
#define PP_DITHER_EN 0x000
|
||||
#define PP_DITHER_BITDEPTH 0x004
|
||||
@ -245,6 +248,32 @@ static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
|
||||
return line;
|
||||
}
|
||||
|
||||
static int dpu_hw_pp_dsc_enable(struct dpu_hw_pingpong *pp)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &pp->hw;
|
||||
|
||||
DPU_REG_WRITE(c, PP_DSC_MODE, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_hw_pp_dsc_disable(struct dpu_hw_pingpong *pp)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &pp->hw;
|
||||
|
||||
DPU_REG_WRITE(c, PP_DSC_MODE, 0);
|
||||
}
|
||||
|
||||
static int dpu_hw_pp_setup_dsc(struct dpu_hw_pingpong *pp)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *pp_c = &pp->hw;
|
||||
int data;
|
||||
|
||||
data = DPU_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP);
|
||||
data |= BIT(18); /* endian flip */
|
||||
DPU_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _setup_pingpong_ops(struct dpu_hw_pingpong *c,
|
||||
unsigned long features)
|
||||
{
|
||||
@ -256,6 +285,9 @@ static void _setup_pingpong_ops(struct dpu_hw_pingpong *c,
|
||||
c->ops.get_autorefresh = dpu_hw_pp_get_autorefresh_config;
|
||||
c->ops.poll_timeout_wr_ptr = dpu_hw_pp_poll_timeout_wr_ptr;
|
||||
c->ops.get_line_count = dpu_hw_pp_get_line_count;
|
||||
c->ops.setup_dsc = dpu_hw_pp_setup_dsc;
|
||||
c->ops.enable_dsc = dpu_hw_pp_dsc_enable;
|
||||
c->ops.disable_dsc = dpu_hw_pp_dsc_disable;
|
||||
|
||||
if (test_bit(DPU_PINGPONG_DITHER, &features))
|
||||
c->ops.setup_dither = dpu_hw_pp_setup_dither;
|
||||
|
@ -124,6 +124,20 @@ struct dpu_hw_pingpong_ops {
|
||||
*/
|
||||
void (*setup_dither)(struct dpu_hw_pingpong *pp,
|
||||
struct dpu_hw_dither_cfg *cfg);
|
||||
/**
|
||||
* Enable DSC
|
||||
*/
|
||||
int (*enable_dsc)(struct dpu_hw_pingpong *pp);
|
||||
|
||||
/**
|
||||
* Disable DSC
|
||||
*/
|
||||
void (*disable_dsc)(struct dpu_hw_pingpong *pp);
|
||||
|
||||
/**
|
||||
* Setup DSC
|
||||
*/
|
||||
int (*setup_dsc)(struct dpu_hw_pingpong *pp);
|
||||
};
|
||||
|
||||
struct dpu_hw_merge_3d;
|
||||
|
@ -627,7 +627,7 @@ static void dpu_hw_sspp_setup_qos_ctrl(struct dpu_hw_pipe *ctx,
|
||||
}
|
||||
|
||||
static void dpu_hw_sspp_setup_cdp(struct dpu_hw_pipe *ctx,
|
||||
struct dpu_hw_pipe_cdp_cfg *cfg,
|
||||
struct dpu_hw_cdp_cfg *cfg,
|
||||
enum dpu_sspp_multirect_index index)
|
||||
{
|
||||
u32 idx;
|
||||
|
@ -192,22 +192,6 @@ enum {
|
||||
DPU_SSPP_CDP_PRELOAD_AHEAD_64
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_pipe_cdp_cfg : CDP configuration
|
||||
* @enable: true to enable CDP
|
||||
* @ubwc_meta_enable: true to enable ubwc metadata preload
|
||||
* @tile_amortize_enable: true to enable amortization control for tile format
|
||||
* @preload_ahead: number of request to preload ahead
|
||||
* DPU_SSPP_CDP_PRELOAD_AHEAD_32,
|
||||
* DPU_SSPP_CDP_PRELOAD_AHEAD_64
|
||||
*/
|
||||
struct dpu_hw_pipe_cdp_cfg {
|
||||
bool enable;
|
||||
bool ubwc_meta_enable;
|
||||
bool tile_amortize_enable;
|
||||
u32 preload_ahead;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_pipe_ts_cfg - traffic shaper configuration
|
||||
* @size: size to prefill in bytes, or zero to disable
|
||||
@ -359,7 +343,7 @@ struct dpu_hw_sspp_ops {
|
||||
* @index: rectangle index in multirect
|
||||
*/
|
||||
void (*setup_cdp)(struct dpu_hw_pipe *ctx,
|
||||
struct dpu_hw_pipe_cdp_cfg *cfg,
|
||||
struct dpu_hw_cdp_cfg *cfg,
|
||||
enum dpu_sspp_multirect_index index);
|
||||
};
|
||||
|
||||
|
@ -422,3 +422,28 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map *c,
|
||||
DPU_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]);
|
||||
DPU_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* _dpu_hw_get_qos_lut - get LUT mapping based on fill level
|
||||
* @tbl: Pointer to LUT table
|
||||
* @total_fl: fill level
|
||||
* Return: LUT setting corresponding to the fill level
|
||||
*/
|
||||
u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
|
||||
u32 total_fl)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!tbl || !tbl->nentry || !tbl->entries)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < tbl->nentry; i++)
|
||||
if (total_fl <= tbl->entries[i].fl)
|
||||
return tbl->entries[i].lut;
|
||||
|
||||
/* if last fl is zero, use as default */
|
||||
if (!tbl->entries[i-1].fl)
|
||||
return tbl->entries[i-1].lut;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include "dpu_hw_mdss.h"
|
||||
#include "dpu_hw_catalog.h"
|
||||
|
||||
#define REG_MASK(n) ((BIT(n)) - 1)
|
||||
|
||||
@ -298,6 +299,21 @@ struct dpu_drm_scaler_v2 {
|
||||
struct dpu_drm_de_v1 de;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_cdp_cfg : CDP configuration
|
||||
* @enable: true to enable CDP
|
||||
* @ubwc_meta_enable: true to enable ubwc metadata preload
|
||||
* @tile_amortize_enable: true to enable amortization control for tile format
|
||||
* @preload_ahead: number of request to preload ahead
|
||||
* DPU_*_CDP_PRELOAD_AHEAD_32,
|
||||
* DPU_*_CDP_PRELOAD_AHEAD_64
|
||||
*/
|
||||
struct dpu_hw_cdp_cfg {
|
||||
bool enable;
|
||||
bool ubwc_meta_enable;
|
||||
bool tile_amortize_enable;
|
||||
u32 preload_ahead;
|
||||
};
|
||||
|
||||
u32 *dpu_hw_util_get_log_mask_ptr(void);
|
||||
|
||||
@ -324,4 +340,7 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map *c,
|
||||
u32 csc_reg_off,
|
||||
const struct dpu_csc_cfg *data, bool csc10);
|
||||
|
||||
u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
|
||||
u32 total_fl);
|
||||
|
||||
#endif /* _DPU_HW_UTIL_H */
|
||||
|
279
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
Normal file
279
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
Normal file
@ -0,0 +1,279 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
|
||||
*/
|
||||
|
||||
#include "dpu_hw_mdss.h"
|
||||
#include "dpu_hwio.h"
|
||||
#include "dpu_hw_catalog.h"
|
||||
#include "dpu_hw_wb.h"
|
||||
#include "dpu_formats.h"
|
||||
#include "dpu_kms.h"
|
||||
|
||||
#define WB_DST_FORMAT 0x000
|
||||
#define WB_DST_OP_MODE 0x004
|
||||
#define WB_DST_PACK_PATTERN 0x008
|
||||
#define WB_DST0_ADDR 0x00C
|
||||
#define WB_DST1_ADDR 0x010
|
||||
#define WB_DST2_ADDR 0x014
|
||||
#define WB_DST3_ADDR 0x018
|
||||
#define WB_DST_YSTRIDE0 0x01C
|
||||
#define WB_DST_YSTRIDE1 0x020
|
||||
#define WB_DST_YSTRIDE1 0x020
|
||||
#define WB_DST_DITHER_BITDEPTH 0x024
|
||||
#define WB_DST_MATRIX_ROW0 0x030
|
||||
#define WB_DST_MATRIX_ROW1 0x034
|
||||
#define WB_DST_MATRIX_ROW2 0x038
|
||||
#define WB_DST_MATRIX_ROW3 0x03C
|
||||
#define WB_DST_WRITE_CONFIG 0x048
|
||||
#define WB_ROTATION_DNSCALER 0x050
|
||||
#define WB_ROTATOR_PIPE_DOWNSCALER 0x054
|
||||
#define WB_N16_INIT_PHASE_X_C03 0x060
|
||||
#define WB_N16_INIT_PHASE_X_C12 0x064
|
||||
#define WB_N16_INIT_PHASE_Y_C03 0x068
|
||||
#define WB_N16_INIT_PHASE_Y_C12 0x06C
|
||||
#define WB_OUT_SIZE 0x074
|
||||
#define WB_ALPHA_X_VALUE 0x078
|
||||
#define WB_DANGER_LUT 0x084
|
||||
#define WB_SAFE_LUT 0x088
|
||||
#define WB_QOS_CTRL 0x090
|
||||
#define WB_CREQ_LUT_0 0x098
|
||||
#define WB_CREQ_LUT_1 0x09C
|
||||
#define WB_UBWC_STATIC_CTRL 0x144
|
||||
#define WB_MUX 0x150
|
||||
#define WB_CROP_CTRL 0x154
|
||||
#define WB_CROP_OFFSET 0x158
|
||||
#define WB_CSC_BASE 0x260
|
||||
#define WB_DST_ADDR_SW_STATUS 0x2B0
|
||||
#define WB_CDP_CNTL 0x2B4
|
||||
#define WB_OUT_IMAGE_SIZE 0x2C0
|
||||
#define WB_OUT_XY 0x2C4
|
||||
|
||||
/* WB_QOS_CTRL */
|
||||
#define WB_QOS_CTRL_DANGER_SAFE_EN BIT(0)
|
||||
|
||||
static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
|
||||
const struct dpu_mdss_cfg *m, void __iomem *addr,
|
||||
struct dpu_hw_blk_reg_map *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < m->wb_count; i++) {
|
||||
if (wb == m->wb[i].id) {
|
||||
b->base_off = addr;
|
||||
b->blk_off = m->wb[i].base;
|
||||
b->length = m->wb[i].len;
|
||||
b->hwversion = m->hwversion;
|
||||
return &m->wb[i];
|
||||
}
|
||||
}
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_cfg *data)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
|
||||
DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
|
||||
DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
|
||||
DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
|
||||
DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_cfg *data)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
const struct dpu_format *fmt = data->dest.format;
|
||||
u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
|
||||
u32 write_config = 0;
|
||||
u32 opmode = 0;
|
||||
u32 dst_addr_sw = 0;
|
||||
|
||||
chroma_samp = fmt->chroma_sample;
|
||||
|
||||
dst_format = (chroma_samp << 23) |
|
||||
(fmt->fetch_planes << 19) |
|
||||
(fmt->bits[C3_ALPHA] << 6) |
|
||||
(fmt->bits[C2_R_Cr] << 4) |
|
||||
(fmt->bits[C1_B_Cb] << 2) |
|
||||
(fmt->bits[C0_G_Y] << 0);
|
||||
|
||||
if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
|
||||
dst_format |= BIT(8); /* DSTC3_EN */
|
||||
if (!fmt->alpha_enable ||
|
||||
!(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
|
||||
dst_format |= BIT(14); /* DST_ALPHA_X */
|
||||
}
|
||||
|
||||
pattern = (fmt->element[3] << 24) |
|
||||
(fmt->element[2] << 16) |
|
||||
(fmt->element[1] << 8) |
|
||||
(fmt->element[0] << 0);
|
||||
|
||||
dst_format |= (fmt->unpack_align_msb << 18) |
|
||||
(fmt->unpack_tight << 17) |
|
||||
((fmt->unpack_count - 1) << 12) |
|
||||
((fmt->bpp - 1) << 9);
|
||||
|
||||
ystride0 = data->dest.plane_pitch[0] |
|
||||
(data->dest.plane_pitch[1] << 16);
|
||||
ystride1 = data->dest.plane_pitch[2] |
|
||||
(data->dest.plane_pitch[3] << 16);
|
||||
|
||||
if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
|
||||
outsize = (drm_rect_height(&data->roi) << 16) | drm_rect_width(&data->roi);
|
||||
else
|
||||
outsize = (data->dest.height << 16) | data->dest.width;
|
||||
|
||||
DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
|
||||
DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
|
||||
DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
|
||||
DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
|
||||
DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
|
||||
DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
|
||||
DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
|
||||
DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
|
||||
DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg *wb)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 image_size, out_size, out_xy;
|
||||
|
||||
image_size = (wb->dest.height << 16) | wb->dest.width;
|
||||
out_xy = 0;
|
||||
out_size = (drm_rect_height(&wb->roi) << 16) | drm_rect_width(&wb->roi);
|
||||
|
||||
DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
|
||||
DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
|
||||
DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_qos_cfg *cfg)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 qos_ctrl = 0;
|
||||
|
||||
if (!ctx || !cfg)
|
||||
return;
|
||||
|
||||
DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
|
||||
DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
|
||||
|
||||
/*
|
||||
* for chipsets not using DPU_WB_QOS_8LVL but still using DPU
|
||||
* driver such as msm8998, the reset value of WB_CREQ_LUT is
|
||||
* sufficient for writeback to work. SW doesn't need to explicitly
|
||||
* program a value.
|
||||
*/
|
||||
if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
|
||||
DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
|
||||
DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
|
||||
}
|
||||
|
||||
if (cfg->danger_safe_en)
|
||||
qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
|
||||
|
||||
DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_cdp_cfg *cfg)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c;
|
||||
u32 cdp_cntl = 0;
|
||||
|
||||
if (!ctx || !cfg)
|
||||
return;
|
||||
|
||||
c = &ctx->hw;
|
||||
|
||||
if (cfg->enable)
|
||||
cdp_cntl |= BIT(0);
|
||||
if (cfg->ubwc_meta_enable)
|
||||
cdp_cntl |= BIT(1);
|
||||
if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
|
||||
cdp_cntl |= BIT(3);
|
||||
|
||||
DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
|
||||
}
|
||||
|
||||
static void dpu_hw_wb_bind_pingpong_blk(
|
||||
struct dpu_hw_wb *ctx,
|
||||
bool enable, const enum dpu_pingpong pp)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c;
|
||||
int mux_cfg;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
c = &ctx->hw;
|
||||
|
||||
mux_cfg = DPU_REG_READ(c, WB_MUX);
|
||||
mux_cfg &= ~0xf;
|
||||
|
||||
if (enable)
|
||||
mux_cfg |= (pp - PINGPONG_0) & 0x7;
|
||||
else
|
||||
mux_cfg |= 0xf;
|
||||
|
||||
DPU_REG_WRITE(c, WB_MUX, mux_cfg);
|
||||
}
|
||||
|
||||
static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
|
||||
unsigned long features)
|
||||
{
|
||||
ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
|
||||
ops->setup_outformat = dpu_hw_wb_setup_format;
|
||||
|
||||
if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
|
||||
ops->setup_roi = dpu_hw_wb_roi;
|
||||
|
||||
if (test_bit(DPU_WB_QOS, &features))
|
||||
ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
|
||||
|
||||
if (test_bit(DPU_WB_CDP, &features))
|
||||
ops->setup_cdp = dpu_hw_wb_setup_cdp;
|
||||
|
||||
if (test_bit(DPU_WB_INPUT_CTRL, &features))
|
||||
ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
|
||||
}
|
||||
|
||||
struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
|
||||
void __iomem *addr, const struct dpu_mdss_cfg *m)
|
||||
{
|
||||
struct dpu_hw_wb *c;
|
||||
const struct dpu_wb_cfg *cfg;
|
||||
|
||||
if (!addr || !m)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cfg = _wb_offset(idx, m, addr, &c->hw);
|
||||
if (IS_ERR(cfg)) {
|
||||
WARN(1, "Unable to find wb idx=%d\n", idx);
|
||||
kfree(c);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* Assign ops */
|
||||
c->mdp = &m->mdp[0];
|
||||
c->idx = idx;
|
||||
c->caps = cfg;
|
||||
_setup_wb_ops(&c->ops, c->caps->features);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
|
||||
{
|
||||
kfree(hw_wb);
|
||||
}
|
115
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
Normal file
115
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef _DPU_HW_WB_H
|
||||
#define _DPU_HW_WB_H
|
||||
|
||||
#include "dpu_hw_catalog.h"
|
||||
#include "dpu_hw_mdss.h"
|
||||
#include "dpu_hw_top.h"
|
||||
#include "dpu_hw_util.h"
|
||||
#include "dpu_hw_pingpong.h"
|
||||
|
||||
struct dpu_hw_wb;
|
||||
|
||||
struct dpu_hw_wb_cfg {
|
||||
struct dpu_hw_fmt_layout dest;
|
||||
enum dpu_intf_mode intf_mode;
|
||||
struct drm_rect roi;
|
||||
struct drm_rect crop;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum CDP preload ahead address size
|
||||
*/
|
||||
enum {
|
||||
DPU_WB_CDP_PRELOAD_AHEAD_32,
|
||||
DPU_WB_CDP_PRELOAD_AHEAD_64
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
|
||||
* @danger_lut: LUT for generate danger level based on fill level
|
||||
* @safe_lut: LUT for generate safe level based on fill level
|
||||
* @creq_lut: LUT for generate creq level based on fill level
|
||||
* @danger_safe_en: enable danger safe generation
|
||||
*/
|
||||
struct dpu_hw_wb_qos_cfg {
|
||||
u32 danger_lut;
|
||||
u32 safe_lut;
|
||||
u64 creq_lut;
|
||||
bool danger_safe_en;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* struct dpu_hw_wb_ops : Interface to the wb hw driver functions
|
||||
* Assumption is these functions will be called after clocks are enabled
|
||||
* @setup_outaddress: setup output address from the writeback job
|
||||
* @setup_outformat: setup output format of writeback block from writeback job
|
||||
* @setup_qos_lut: setup qos LUT for writeback block based on input
|
||||
* @setup_cdp: setup chroma down prefetch block for writeback block
|
||||
* @bind_pingpong_blk: enable/disable the connection with ping-pong block
|
||||
*/
|
||||
struct dpu_hw_wb_ops {
|
||||
void (*setup_outaddress)(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_cfg *wb);
|
||||
|
||||
void (*setup_outformat)(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_cfg *wb);
|
||||
|
||||
void (*setup_roi)(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_cfg *wb);
|
||||
|
||||
void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_wb_qos_cfg *cfg);
|
||||
|
||||
void (*setup_cdp)(struct dpu_hw_wb *ctx,
|
||||
struct dpu_hw_cdp_cfg *cfg);
|
||||
|
||||
void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
|
||||
bool enable, const enum dpu_pingpong pp);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_wb : WB driver object
|
||||
* @hw: block hardware details
|
||||
* @mdp: pointer to associated mdp portion of the catalog
|
||||
* @idx: hardware index number within type
|
||||
* @wb_hw_caps: hardware capabilities
|
||||
* @ops: function pointers
|
||||
* @hw_mdp: MDP top level hardware block
|
||||
*/
|
||||
struct dpu_hw_wb {
|
||||
struct dpu_hw_blk_reg_map hw;
|
||||
const struct dpu_mdp_cfg *mdp;
|
||||
|
||||
/* wb path */
|
||||
int idx;
|
||||
const struct dpu_wb_cfg *caps;
|
||||
|
||||
/* ops */
|
||||
struct dpu_hw_wb_ops ops;
|
||||
|
||||
struct dpu_hw_mdp *hw_mdp;
|
||||
};
|
||||
|
||||
/**
|
||||
* dpu_hw_wb_init(): Initializes and return writeback hw driver object.
|
||||
* @idx: wb_path index for which driver object is required
|
||||
* @addr: mapped register io address of MDP
|
||||
* @m : pointer to mdss catalog data
|
||||
*/
|
||||
struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
|
||||
void __iomem *addr,
|
||||
const struct dpu_mdss_cfg *m);
|
||||
|
||||
/**
|
||||
* dpu_hw_wb_destroy(): Destroy writeback hw driver object.
|
||||
* @hw_wb: Pointer to writeback hw driver object
|
||||
*/
|
||||
void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
|
||||
|
||||
#endif /*_DPU_HW_WB_H */
|
@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
|
||||
@ -15,6 +17,7 @@
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <drm/drm_writeback.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_mmu.h"
|
||||
@ -29,6 +32,7 @@
|
||||
#include "dpu_kms.h"
|
||||
#include "dpu_plane.h"
|
||||
#include "dpu_vbif.h"
|
||||
#include "dpu_writeback.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "dpu_trace.h"
|
||||
@ -380,9 +384,13 @@ static int dpu_kms_parse_data_bus_icc_path(struct dpu_kms *dpu_kms)
|
||||
struct icc_path *path0;
|
||||
struct icc_path *path1;
|
||||
struct drm_device *dev = dpu_kms->dev;
|
||||
struct device *dpu_dev = dev->dev;
|
||||
struct device *mdss_dev = dpu_dev->parent;
|
||||
|
||||
path0 = of_icc_get(dev->dev, "mdp0-mem");
|
||||
path1 = of_icc_get(dev->dev, "mdp1-mem");
|
||||
/* Interconnects are a part of MDSS device tree binding, not the
|
||||
* MDP/DPU device. */
|
||||
path0 = of_icc_get(mdss_dev, "mdp0-mem");
|
||||
path1 = of_icc_get(mdss_dev, "mdp1-mem");
|
||||
|
||||
if (IS_ERR_OR_NULL(path0))
|
||||
return PTR_ERR_OR_ZERO(path0);
|
||||
@ -565,8 +573,6 @@ static int _dpu_kms_initialize_dsi(struct drm_device *dev,
|
||||
return PTR_ERR(encoder);
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.intf_type = encoder->encoder_type;
|
||||
|
||||
@ -582,6 +588,8 @@ static int _dpu_kms_initialize_dsi(struct drm_device *dev,
|
||||
MSM_DISPLAY_CAP_CMD_MODE :
|
||||
MSM_DISPLAY_CAP_VID_MODE;
|
||||
|
||||
info.dsc = msm_dsi_get_dsc_config(priv->dsi[i]);
|
||||
|
||||
if (msm_dsi_is_bonded_dsi(priv->dsi[i]) && priv->dsi[other]) {
|
||||
rc = msm_dsi_modeset_init(priv->dsi[other], dev, encoder);
|
||||
if (rc) {
|
||||
@ -629,8 +637,6 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
|
||||
return rc;
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
info.num_of_h_tiles = 1;
|
||||
info.h_tile_instance[0] = i;
|
||||
info.capabilities = MSM_DISPLAY_CAP_VID_MODE;
|
||||
@ -646,6 +652,45 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dpu_kms_initialize_writeback(struct drm_device *dev,
|
||||
struct msm_drm_private *priv, struct dpu_kms *dpu_kms,
|
||||
const u32 *wb_formats, int n_formats)
|
||||
{
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct msm_display_info info;
|
||||
int rc;
|
||||
|
||||
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_VIRTUAL);
|
||||
if (IS_ERR(encoder)) {
|
||||
DPU_ERROR("encoder init failed for dsi display\n");
|
||||
return PTR_ERR(encoder);
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
rc = dpu_writeback_init(dev, encoder, wb_formats,
|
||||
n_formats);
|
||||
if (rc) {
|
||||
DPU_ERROR("dpu_writeback_init, rc = %d\n", rc);
|
||||
drm_encoder_cleanup(encoder);
|
||||
return rc;
|
||||
}
|
||||
|
||||
info.num_of_h_tiles = 1;
|
||||
/* use only WB idx 2 instance for DPU */
|
||||
info.h_tile_instance[0] = WB_2;
|
||||
info.intf_type = encoder->encoder_type;
|
||||
|
||||
rc = dpu_encoder_setup(dev, encoder, &info);
|
||||
if (rc) {
|
||||
DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
|
||||
encoder->base.id, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _dpu_kms_setup_displays - create encoders, bridges and connectors
|
||||
* for underlying displays
|
||||
@ -659,6 +704,7 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
|
||||
struct dpu_kms *dpu_kms)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
rc = _dpu_kms_initialize_dsi(dev, priv, dpu_kms);
|
||||
if (rc) {
|
||||
@ -672,39 +718,33 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Since WB isn't a driver check the catalog before initializing */
|
||||
if (dpu_kms->catalog->wb_count) {
|
||||
for (i = 0; i < dpu_kms->catalog->wb_count; i++) {
|
||||
if (dpu_kms->catalog->wb[i].id == WB_2) {
|
||||
rc = _dpu_kms_initialize_writeback(dev, priv, dpu_kms,
|
||||
dpu_kms->catalog->wb[i].format_list,
|
||||
dpu_kms->catalog->wb[i].num_formats);
|
||||
if (rc) {
|
||||
DPU_ERROR("initialize_WB failed, rc = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void _dpu_kms_drm_obj_destroy(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
struct msm_drm_private *priv;
|
||||
int i;
|
||||
|
||||
priv = dpu_kms->dev->dev_private;
|
||||
|
||||
for (i = 0; i < priv->num_crtcs; i++)
|
||||
priv->crtcs[i]->funcs->destroy(priv->crtcs[i]);
|
||||
priv->num_crtcs = 0;
|
||||
|
||||
for (i = 0; i < priv->num_planes; i++)
|
||||
priv->planes[i]->funcs->destroy(priv->planes[i]);
|
||||
priv->num_planes = 0;
|
||||
|
||||
for (i = 0; i < priv->num_connectors; i++)
|
||||
priv->connectors[i]->funcs->destroy(priv->connectors[i]);
|
||||
priv->num_connectors = 0;
|
||||
|
||||
for (i = 0; i < priv->num_encoders; i++)
|
||||
priv->encoders[i]->funcs->destroy(priv->encoders[i]);
|
||||
priv->num_encoders = 0;
|
||||
}
|
||||
|
||||
#define MAX_PLANES 20
|
||||
static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct drm_plane *primary_planes[MAX_PLANES], *plane;
|
||||
struct drm_plane *cursor_planes[MAX_PLANES] = { NULL };
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
unsigned int num_encoders;
|
||||
|
||||
struct msm_drm_private *priv;
|
||||
struct dpu_mdss_cfg *catalog;
|
||||
@ -721,9 +761,13 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
|
||||
*/
|
||||
ret = _dpu_kms_setup_displays(dev, priv, dpu_kms);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
max_crtc_count = min(catalog->mixer_count, priv->num_encoders);
|
||||
num_encoders = 0;
|
||||
drm_for_each_encoder(encoder, dev)
|
||||
num_encoders++;
|
||||
|
||||
max_crtc_count = min(catalog->mixer_count, num_encoders);
|
||||
|
||||
/* Create the planes, keeping track of one primary/cursor per crtc */
|
||||
for (i = 0; i < catalog->sspp_count; i++) {
|
||||
@ -746,9 +790,8 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
|
||||
if (IS_ERR(plane)) {
|
||||
DPU_ERROR("dpu_plane_init failed\n");
|
||||
ret = PTR_ERR(plane);
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
priv->planes[priv->num_planes++] = plane;
|
||||
|
||||
if (type == DRM_PLANE_TYPE_CURSOR)
|
||||
cursor_planes[cursor_planes_idx++] = plane;
|
||||
@ -763,19 +806,16 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
|
||||
crtc = dpu_crtc_init(dev, primary_planes[i], cursor_planes[i]);
|
||||
if (IS_ERR(crtc)) {
|
||||
ret = PTR_ERR(crtc);
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
priv->crtcs[priv->num_crtcs++] = crtc;
|
||||
}
|
||||
|
||||
/* All CRTCs are compatible with all encoders */
|
||||
for (i = 0; i < priv->num_encoders; i++)
|
||||
priv->encoders[i]->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
||||
drm_for_each_encoder(encoder, dev)
|
||||
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
_dpu_kms_drm_obj_destroy(dpu_kms);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms)
|
||||
@ -793,8 +833,10 @@ static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms)
|
||||
for (i = 0; i < dpu_kms->catalog->vbif_count; i++) {
|
||||
u32 vbif_idx = dpu_kms->catalog->vbif[i].id;
|
||||
|
||||
if ((vbif_idx < VBIF_MAX) && dpu_kms->hw_vbif[vbif_idx])
|
||||
if ((vbif_idx < VBIF_MAX) && dpu_kms->hw_vbif[vbif_idx]) {
|
||||
dpu_hw_vbif_destroy(dpu_kms->hw_vbif[vbif_idx]);
|
||||
dpu_kms->hw_vbif[vbif_idx] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -837,20 +879,9 @@ static void dpu_kms_destroy(struct msm_kms *kms)
|
||||
_dpu_kms_hw_destroy(dpu_kms);
|
||||
|
||||
msm_kms_destroy(&dpu_kms->base);
|
||||
}
|
||||
|
||||
static irqreturn_t dpu_irq(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
|
||||
return dpu_core_irq(dpu_kms);
|
||||
}
|
||||
|
||||
static void dpu_irq_preinstall(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
|
||||
dpu_core_irq_preinstall(dpu_kms);
|
||||
if (dpu_kms->rpm_enabled)
|
||||
pm_runtime_disable(&dpu_kms->pdev->dev);
|
||||
}
|
||||
|
||||
static int dpu_irq_postinstall(struct msm_kms *kms)
|
||||
@ -872,13 +903,6 @@ static int dpu_irq_postinstall(struct msm_kms *kms)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_irq_uninstall(struct msm_kms *kms)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
|
||||
dpu_core_irq_uninstall(dpu_kms);
|
||||
}
|
||||
|
||||
static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_kms *kms)
|
||||
{
|
||||
int i;
|
||||
@ -923,6 +947,11 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
|
||||
msm_disp_snapshot_add_block(disp_state, cat->mixer[i].len,
|
||||
dpu_kms->mmio + cat->mixer[i].base, "lm_%d", i);
|
||||
|
||||
/* dump WB sub-blocks HW regs info */
|
||||
for (i = 0; i < cat->wb_count; i++)
|
||||
msm_disp_snapshot_add_block(disp_state, cat->wb[i].len,
|
||||
dpu_kms->mmio + cat->wb[i].base, "wb_%d", i);
|
||||
|
||||
msm_disp_snapshot_add_block(disp_state, top->hw.length,
|
||||
dpu_kms->mmio + top->hw.blk_off, "top");
|
||||
|
||||
@ -931,10 +960,10 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
|
||||
|
||||
static const struct msm_kms_funcs kms_funcs = {
|
||||
.hw_init = dpu_kms_hw_init,
|
||||
.irq_preinstall = dpu_irq_preinstall,
|
||||
.irq_preinstall = dpu_core_irq_preinstall,
|
||||
.irq_postinstall = dpu_irq_postinstall,
|
||||
.irq_uninstall = dpu_irq_uninstall,
|
||||
.irq = dpu_irq,
|
||||
.irq_uninstall = dpu_core_irq_uninstall,
|
||||
.irq = dpu_core_irq,
|
||||
.enable_commit = dpu_kms_enable_commit,
|
||||
.disable_commit = dpu_kms_disable_commit,
|
||||
.vsync_time = dpu_kms_vsync_time,
|
||||
@ -973,12 +1002,16 @@ static int _dpu_kms_mmu_init(struct dpu_kms *dpu_kms)
|
||||
struct iommu_domain *domain;
|
||||
struct msm_gem_address_space *aspace;
|
||||
struct msm_mmu *mmu;
|
||||
struct device *dpu_dev = dpu_kms->dev->dev;
|
||||
struct device *mdss_dev = dpu_dev->parent;
|
||||
|
||||
domain = iommu_domain_alloc(&platform_bus_type);
|
||||
if (!domain)
|
||||
return 0;
|
||||
|
||||
mmu = msm_iommu_new(dpu_kms->dev->dev, domain);
|
||||
/* IOMMUs are a part of MDSS device tree binding, not the
|
||||
* MDP/DPU device. */
|
||||
mmu = msm_iommu_new(mdss_dev, domain);
|
||||
if (IS_ERR(mmu)) {
|
||||
iommu_domain_free(domain);
|
||||
return PTR_ERR(mmu);
|
||||
@ -1172,37 +1205,16 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct msm_kms *dpu_kms_init(struct drm_device *dev)
|
||||
static int dpu_kms_init(struct drm_device *ddev)
|
||||
{
|
||||
struct msm_drm_private *priv;
|
||||
struct msm_drm_private *priv = ddev->dev_private;
|
||||
struct device *dev = ddev->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dpu_kms *dpu_kms;
|
||||
int irq;
|
||||
|
||||
if (!dev) {
|
||||
DPU_ERROR("drm device node invalid\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
priv = dev->dev_private;
|
||||
dpu_kms = to_dpu_kms(priv->kms);
|
||||
|
||||
irq = irq_of_parse_and_map(dpu_kms->pdev->dev.of_node, 0);
|
||||
if (irq < 0) {
|
||||
DPU_ERROR("failed to get irq: %d\n", irq);
|
||||
return ERR_PTR(irq);
|
||||
}
|
||||
dpu_kms->base.irq = irq;
|
||||
|
||||
return &dpu_kms->base;
|
||||
}
|
||||
|
||||
static int dpu_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(master);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *ddev = priv->dev;
|
||||
struct dpu_kms *dpu_kms;
|
||||
struct dev_pm_opp *opp;
|
||||
int ret = 0;
|
||||
unsigned long max_freq = ULONG_MAX;
|
||||
|
||||
dpu_kms = devm_kzalloc(&pdev->dev, sizeof(*dpu_kms), GFP_KERNEL);
|
||||
if (!dpu_kms)
|
||||
@ -1225,7 +1237,11 @@ static int dpu_bind(struct device *dev, struct device *master, void *data)
|
||||
}
|
||||
dpu_kms->num_clocks = ret;
|
||||
|
||||
platform_set_drvdata(pdev, dpu_kms);
|
||||
opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
|
||||
if (!IS_ERR(opp))
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
dev_pm_opp_set_rate(dev, max_freq);
|
||||
|
||||
ret = msm_kms_init(&dpu_kms->base, &kms_funcs);
|
||||
if (ret) {
|
||||
@ -1240,31 +1256,25 @@ static int dpu_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
priv->kms = &dpu_kms->base;
|
||||
|
||||
return ret;
|
||||
irq = irq_of_parse_and_map(dpu_kms->pdev->dev.of_node, 0);
|
||||
if (!irq) {
|
||||
DPU_ERROR("failed to get irq\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dpu_kms->base.irq = irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dpu_kms *dpu_kms = platform_get_drvdata(pdev);
|
||||
|
||||
if (dpu_kms->rpm_enabled)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct component_ops dpu_ops = {
|
||||
.bind = dpu_bind,
|
||||
.unbind = dpu_unbind,
|
||||
};
|
||||
|
||||
static int dpu_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &dpu_ops);
|
||||
return msm_drv_probe(&pdev->dev, dpu_kms_init);
|
||||
}
|
||||
|
||||
static int dpu_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &dpu_ops);
|
||||
component_master_del(&pdev->dev, &msm_drm_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1272,7 +1282,8 @@ static int __maybe_unused dpu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dpu_kms *dpu_kms = platform_get_drvdata(pdev);
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms);
|
||||
|
||||
/* Drop the performance state vote */
|
||||
dev_pm_opp_set_rate(dev, 0);
|
||||
@ -1288,7 +1299,8 @@ static int __maybe_unused dpu_runtime_resume(struct device *dev)
|
||||
{
|
||||
int rc = -1;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dpu_kms *dpu_kms = platform_get_drvdata(pdev);
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms);
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_device *ddev;
|
||||
int i;
|
||||
@ -1318,9 +1330,11 @@ static const struct dev_pm_ops dpu_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(dpu_runtime_suspend, dpu_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
.prepare = msm_pm_prepare,
|
||||
.complete = msm_pm_complete,
|
||||
};
|
||||
|
||||
const struct of_device_id dpu_dt_match[] = {
|
||||
static const struct of_device_id dpu_dt_match[] = {
|
||||
{ .compatible = "qcom,msm8998-dpu", },
|
||||
{ .compatible = "qcom,qcm2290-dpu", },
|
||||
{ .compatible = "qcom,sdm845-dpu", },
|
||||
@ -1336,6 +1350,7 @@ MODULE_DEVICE_TABLE(of, dpu_dt_match);
|
||||
static struct platform_driver dpu_driver = {
|
||||
.probe = dpu_dev_probe,
|
||||
.remove = dpu_dev_remove,
|
||||
.shutdown = msm_drv_shutdown,
|
||||
.driver = {
|
||||
.name = "msm_dpu",
|
||||
.of_match_table = dpu_dt_match,
|
||||
|
@ -65,18 +65,6 @@
|
||||
|
||||
#define DPU_NAME_SIZE 12
|
||||
|
||||
/*
|
||||
* struct dpu_irq_callback - IRQ callback handlers
|
||||
* @list: list to callback
|
||||
* @func: intr handler
|
||||
* @arg: argument for the handler
|
||||
*/
|
||||
struct dpu_irq_callback {
|
||||
struct list_head list;
|
||||
void (*func)(void *arg, int irq_idx);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct dpu_kms {
|
||||
struct msm_kms base;
|
||||
struct drm_device *dev;
|
||||
@ -145,6 +133,7 @@ struct dpu_global_state {
|
||||
uint32_t mixer_to_enc_id[LM_MAX - LM_0];
|
||||
uint32_t ctl_to_enc_id[CTL_MAX - CTL_0];
|
||||
uint32_t dspp_to_enc_id[DSPP_MAX - DSPP_0];
|
||||
uint32_t dsc_to_enc_id[DSC_MAX - DSC_0];
|
||||
};
|
||||
|
||||
struct dpu_global_state
|
||||
|
@ -1,260 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
* Copyright (c) 2018, The Linux Foundation
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include "dpu_kms.h"
|
||||
|
||||
#define to_dpu_mdss(x) container_of(x, struct dpu_mdss, base)
|
||||
|
||||
#define HW_REV 0x0
|
||||
#define HW_INTR_STATUS 0x0010
|
||||
|
||||
#define UBWC_STATIC 0x144
|
||||
#define UBWC_CTRL_2 0x150
|
||||
#define UBWC_PREDICTION_MODE 0x154
|
||||
|
||||
/* Max BW defined in KBps */
|
||||
#define MAX_BW 6800000
|
||||
|
||||
struct dpu_irq_controller {
|
||||
unsigned long enabled_mask;
|
||||
struct irq_domain *domain;
|
||||
};
|
||||
|
||||
struct dpu_mdss {
|
||||
struct msm_mdss base;
|
||||
void __iomem *mmio;
|
||||
struct clk_bulk_data *clocks;
|
||||
size_t num_clocks;
|
||||
struct dpu_irq_controller irq_controller;
|
||||
};
|
||||
|
||||
static void dpu_mdss_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
u32 interrupts;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
interrupts = readl_relaxed(dpu_mdss->mmio + HW_INTR_STATUS);
|
||||
|
||||
while (interrupts) {
|
||||
irq_hw_number_t hwirq = fls(interrupts) - 1;
|
||||
int rc;
|
||||
|
||||
rc = generic_handle_domain_irq(dpu_mdss->irq_controller.domain,
|
||||
hwirq);
|
||||
if (rc < 0) {
|
||||
DRM_ERROR("handle irq fail: irq=%lu rc=%d\n",
|
||||
hwirq, rc);
|
||||
break;
|
||||
}
|
||||
|
||||
interrupts &= ~(1 << hwirq);
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void dpu_mdss_irq_mask(struct irq_data *irqd)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = irq_data_get_irq_chip_data(irqd);
|
||||
|
||||
/* memory barrier */
|
||||
smp_mb__before_atomic();
|
||||
clear_bit(irqd->hwirq, &dpu_mdss->irq_controller.enabled_mask);
|
||||
/* memory barrier */
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
|
||||
static void dpu_mdss_irq_unmask(struct irq_data *irqd)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = irq_data_get_irq_chip_data(irqd);
|
||||
|
||||
/* memory barrier */
|
||||
smp_mb__before_atomic();
|
||||
set_bit(irqd->hwirq, &dpu_mdss->irq_controller.enabled_mask);
|
||||
/* memory barrier */
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
|
||||
static struct irq_chip dpu_mdss_irq_chip = {
|
||||
.name = "dpu_mdss",
|
||||
.irq_mask = dpu_mdss_irq_mask,
|
||||
.irq_unmask = dpu_mdss_irq_unmask,
|
||||
};
|
||||
|
||||
static struct lock_class_key dpu_mdss_lock_key, dpu_mdss_request_key;
|
||||
|
||||
static int dpu_mdss_irqdomain_map(struct irq_domain *domain,
|
||||
unsigned int irq, irq_hw_number_t hwirq)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = domain->host_data;
|
||||
|
||||
irq_set_lockdep_class(irq, &dpu_mdss_lock_key, &dpu_mdss_request_key);
|
||||
irq_set_chip_and_handler(irq, &dpu_mdss_irq_chip, handle_level_irq);
|
||||
return irq_set_chip_data(irq, dpu_mdss);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops dpu_mdss_irqdomain_ops = {
|
||||
.map = dpu_mdss_irqdomain_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static int _dpu_mdss_irq_domain_add(struct dpu_mdss *dpu_mdss)
|
||||
{
|
||||
struct device *dev;
|
||||
struct irq_domain *domain;
|
||||
|
||||
dev = dpu_mdss->base.dev;
|
||||
|
||||
domain = irq_domain_add_linear(dev->of_node, 32,
|
||||
&dpu_mdss_irqdomain_ops, dpu_mdss);
|
||||
if (!domain) {
|
||||
DPU_ERROR("failed to add irq_domain\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dpu_mdss->irq_controller.enabled_mask = 0;
|
||||
dpu_mdss->irq_controller.domain = domain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dpu_mdss_irq_domain_fini(struct dpu_mdss *dpu_mdss)
|
||||
{
|
||||
if (dpu_mdss->irq_controller.domain) {
|
||||
irq_domain_remove(dpu_mdss->irq_controller.domain);
|
||||
dpu_mdss->irq_controller.domain = NULL;
|
||||
}
|
||||
}
|
||||
static int dpu_mdss_enable(struct msm_mdss *mdss)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = to_dpu_mdss(mdss);
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(dpu_mdss->num_clocks, dpu_mdss->clocks);
|
||||
if (ret) {
|
||||
DPU_ERROR("clock enable failed, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ubwc config is part of the "mdss" region which is not accessible
|
||||
* from the rest of the driver. hardcode known configurations here
|
||||
*/
|
||||
switch (readl_relaxed(dpu_mdss->mmio + HW_REV)) {
|
||||
case DPU_HW_VER_500:
|
||||
case DPU_HW_VER_501:
|
||||
writel_relaxed(0x420, dpu_mdss->mmio + UBWC_STATIC);
|
||||
break;
|
||||
case DPU_HW_VER_600:
|
||||
/* TODO: 0x102e for LP_DDR4 */
|
||||
writel_relaxed(0x103e, dpu_mdss->mmio + UBWC_STATIC);
|
||||
writel_relaxed(2, dpu_mdss->mmio + UBWC_CTRL_2);
|
||||
writel_relaxed(1, dpu_mdss->mmio + UBWC_PREDICTION_MODE);
|
||||
break;
|
||||
case DPU_HW_VER_620:
|
||||
writel_relaxed(0x1e, dpu_mdss->mmio + UBWC_STATIC);
|
||||
break;
|
||||
case DPU_HW_VER_720:
|
||||
writel_relaxed(0x101e, dpu_mdss->mmio + UBWC_STATIC);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dpu_mdss_disable(struct msm_mdss *mdss)
|
||||
{
|
||||
struct dpu_mdss *dpu_mdss = to_dpu_mdss(mdss);
|
||||
|
||||
clk_bulk_disable_unprepare(dpu_mdss->num_clocks, dpu_mdss->clocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_mdss_destroy(struct msm_mdss *mdss)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mdss->dev);
|
||||
struct dpu_mdss *dpu_mdss = to_dpu_mdss(mdss);
|
||||
int irq;
|
||||
|
||||
pm_runtime_suspend(mdss->dev);
|
||||
pm_runtime_disable(mdss->dev);
|
||||
_dpu_mdss_irq_domain_fini(dpu_mdss);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
irq_set_chained_handler_and_data(irq, NULL, NULL);
|
||||
|
||||
if (dpu_mdss->mmio)
|
||||
devm_iounmap(&pdev->dev, dpu_mdss->mmio);
|
||||
dpu_mdss->mmio = NULL;
|
||||
}
|
||||
|
||||
static const struct msm_mdss_funcs mdss_funcs = {
|
||||
.enable = dpu_mdss_enable,
|
||||
.disable = dpu_mdss_disable,
|
||||
.destroy = dpu_mdss_destroy,
|
||||
};
|
||||
|
||||
int dpu_mdss_init(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct dpu_mdss *dpu_mdss;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
dpu_mdss = devm_kzalloc(&pdev->dev, sizeof(*dpu_mdss), GFP_KERNEL);
|
||||
if (!dpu_mdss)
|
||||
return -ENOMEM;
|
||||
|
||||
dpu_mdss->mmio = msm_ioremap(pdev, "mdss");
|
||||
if (IS_ERR(dpu_mdss->mmio))
|
||||
return PTR_ERR(dpu_mdss->mmio);
|
||||
|
||||
DRM_DEBUG("mapped mdss address space @%pK\n", dpu_mdss->mmio);
|
||||
|
||||
ret = devm_clk_bulk_get_all(&pdev->dev, &dpu_mdss->clocks);
|
||||
if (ret < 0) {
|
||||
DPU_ERROR("failed to parse clocks, ret=%d\n", ret);
|
||||
goto clk_parse_err;
|
||||
}
|
||||
dpu_mdss->num_clocks = ret;
|
||||
|
||||
dpu_mdss->base.dev = &pdev->dev;
|
||||
dpu_mdss->base.funcs = &mdss_funcs;
|
||||
|
||||
ret = _dpu_mdss_irq_domain_add(dpu_mdss);
|
||||
if (ret)
|
||||
goto irq_domain_error;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto irq_error;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(irq, dpu_mdss_irq,
|
||||
dpu_mdss);
|
||||
|
||||
priv->mdss = &dpu_mdss->base;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
irq_error:
|
||||
_dpu_mdss_irq_domain_fini(dpu_mdss);
|
||||
irq_domain_error:
|
||||
clk_parse_err:
|
||||
if (dpu_mdss->mmio)
|
||||
devm_iounmap(&pdev->dev, dpu_mdss->mmio);
|
||||
dpu_mdss->mmio = NULL;
|
||||
return ret;
|
||||
}
|
@ -279,31 +279,6 @@ static int _dpu_plane_calc_fill_level(struct drm_plane *plane,
|
||||
return total_fl;
|
||||
}
|
||||
|
||||
/**
|
||||
* _dpu_plane_get_qos_lut - get LUT mapping based on fill level
|
||||
* @tbl: Pointer to LUT table
|
||||
* @total_fl: fill level
|
||||
* Return: LUT setting corresponding to the fill level
|
||||
*/
|
||||
static u64 _dpu_plane_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
|
||||
u32 total_fl)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!tbl || !tbl->nentry || !tbl->entries)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < tbl->nentry; i++)
|
||||
if (total_fl <= tbl->entries[i].fl)
|
||||
return tbl->entries[i].lut;
|
||||
|
||||
/* if last fl is zero, use as default */
|
||||
if (!tbl->entries[i-1].fl)
|
||||
return tbl->entries[i-1].lut;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _dpu_plane_set_qos_lut - set QoS LUT of the given plane
|
||||
* @plane: Pointer to drm plane
|
||||
@ -333,7 +308,7 @@ static void _dpu_plane_set_qos_lut(struct drm_plane *plane,
|
||||
lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;
|
||||
}
|
||||
|
||||
qos_lut = _dpu_plane_get_qos_lut(
|
||||
qos_lut = _dpu_hw_get_qos_lut(
|
||||
&pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl);
|
||||
|
||||
trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0,
|
||||
@ -528,11 +503,19 @@ static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu,
|
||||
struct dpu_plane_state *pstate,
|
||||
uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,
|
||||
struct dpu_hw_scaler3_cfg *scale_cfg,
|
||||
struct dpu_hw_pixel_ext *pixel_ext,
|
||||
const struct dpu_format *fmt,
|
||||
uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)
|
||||
{
|
||||
uint32_t i;
|
||||
bool inline_rotation = pstate->rotation & DRM_MODE_ROTATE_90;
|
||||
|
||||
/*
|
||||
* For inline rotation cases, scaler config is post-rotation,
|
||||
* so swap the dimensions here. However, pixel extension will
|
||||
* need pre-rotation settings.
|
||||
*/
|
||||
if (inline_rotation)
|
||||
swap(src_w, src_h);
|
||||
|
||||
scale_cfg->phase_step_x[DPU_SSPP_COMP_0] =
|
||||
mult_frac((1 << PHASE_STEP_SHIFT), src_w, dst_w);
|
||||
@ -571,11 +554,6 @@ static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu,
|
||||
scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H;
|
||||
scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V;
|
||||
}
|
||||
|
||||
pixel_ext->num_ext_pxls_top[i] =
|
||||
scale_cfg->src_height[i];
|
||||
pixel_ext->num_ext_pxls_left[i] =
|
||||
scale_cfg->src_width[i];
|
||||
}
|
||||
if (!(DPU_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
|
||||
&& (src_w == dst_w))
|
||||
@ -591,6 +569,24 @@ static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu,
|
||||
scale_cfg->enable = 1;
|
||||
}
|
||||
|
||||
static void _dpu_plane_setup_pixel_ext(struct dpu_hw_scaler3_cfg *scale_cfg,
|
||||
struct dpu_hw_pixel_ext *pixel_ext,
|
||||
uint32_t src_w, uint32_t src_h,
|
||||
uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DPU_MAX_PLANES; i++) {
|
||||
if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {
|
||||
src_w /= chroma_subsmpl_h;
|
||||
src_h /= chroma_subsmpl_v;
|
||||
}
|
||||
|
||||
pixel_ext->num_ext_pxls_top[i] = src_h;
|
||||
pixel_ext->num_ext_pxls_left[i] = src_w;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct dpu_csc_cfg dpu_csc_YUV2RGB_601L = {
|
||||
{
|
||||
/* S15.16 format */
|
||||
@ -654,6 +650,10 @@ static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu,
|
||||
const struct drm_format_info *info = drm_format_info(fmt->base.pixel_format);
|
||||
struct dpu_hw_scaler3_cfg scaler3_cfg;
|
||||
struct dpu_hw_pixel_ext pixel_ext;
|
||||
u32 src_width = drm_rect_width(&pipe_cfg->src_rect);
|
||||
u32 src_height = drm_rect_height(&pipe_cfg->src_rect);
|
||||
u32 dst_width = drm_rect_width(&pipe_cfg->dst_rect);
|
||||
u32 dst_height = drm_rect_height(&pipe_cfg->dst_rect);
|
||||
|
||||
memset(&scaler3_cfg, 0, sizeof(scaler3_cfg));
|
||||
memset(&pixel_ext, 0, sizeof(pixel_ext));
|
||||
@ -661,13 +661,17 @@ static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu,
|
||||
/* don't chroma subsample if decimating */
|
||||
/* update scaler. calculate default config for QSEED3 */
|
||||
_dpu_plane_setup_scaler3(pdpu, pstate,
|
||||
drm_rect_width(&pipe_cfg->src_rect),
|
||||
drm_rect_height(&pipe_cfg->src_rect),
|
||||
drm_rect_width(&pipe_cfg->dst_rect),
|
||||
drm_rect_height(&pipe_cfg->dst_rect),
|
||||
&scaler3_cfg, &pixel_ext, fmt,
|
||||
src_width,
|
||||
src_height,
|
||||
dst_width,
|
||||
dst_height,
|
||||
&scaler3_cfg, fmt,
|
||||
info->hsub, info->vsub);
|
||||
|
||||
/* configure pixel extension based on scalar config */
|
||||
_dpu_plane_setup_pixel_ext(&scaler3_cfg, &pixel_ext,
|
||||
src_width, src_height, info->hsub, info->vsub);
|
||||
|
||||
if (pdpu->pipe_hw->ops.setup_pe)
|
||||
pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw,
|
||||
&pixel_ext);
|
||||
@ -956,6 +960,34 @@ static bool dpu_plane_validate_src(struct drm_rect *src,
|
||||
drm_rect_equals(fb_rect, src);
|
||||
}
|
||||
|
||||
static int dpu_plane_check_inline_rotation(struct dpu_plane *pdpu,
|
||||
const struct dpu_sspp_sub_blks *sblk,
|
||||
struct drm_rect src, const struct dpu_format *fmt)
|
||||
{
|
||||
size_t num_formats;
|
||||
const u32 *supported_formats;
|
||||
|
||||
if (!sblk->rotation_cfg) {
|
||||
DPU_ERROR("invalid rotation cfg\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (drm_rect_width(&src) > sblk->rotation_cfg->rot_maxheight) {
|
||||
DPU_DEBUG_PLANE(pdpu, "invalid height for inline rot:%d max:%d\n",
|
||||
src.y2, sblk->rotation_cfg->rot_maxheight);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
supported_formats = sblk->rotation_cfg->rot_format_list;
|
||||
num_formats = sblk->rotation_cfg->rot_num_formats;
|
||||
|
||||
if (!DPU_FORMAT_IS_UBWC(fmt) ||
|
||||
!dpu_find_format(fmt->base.pixel_format, supported_formats, num_formats))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpu_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
@ -968,15 +1000,19 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
|
||||
const struct dpu_format *fmt;
|
||||
struct drm_rect src, dst, fb_rect = { 0 };
|
||||
uint32_t min_src_size, max_linewidth;
|
||||
unsigned int rotation;
|
||||
uint32_t supported_rotations;
|
||||
const struct dpu_sspp_cfg *pipe_hw_caps = pdpu->pipe_hw->cap;
|
||||
const struct dpu_sspp_sub_blks *sblk = pdpu->pipe_hw->cap->sblk;
|
||||
|
||||
if (new_plane_state->crtc)
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
|
||||
min_scale = FRAC_16_16(1, pdpu->pipe_hw->cap->sblk->maxupscale);
|
||||
min_scale = FRAC_16_16(1, sblk->maxupscale);
|
||||
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
|
||||
min_scale,
|
||||
pdpu->pipe_hw->cap->sblk->maxdwnscale << 16,
|
||||
sblk->maxdwnscale << 16,
|
||||
true, true);
|
||||
if (ret) {
|
||||
DPU_DEBUG_PLANE(pdpu, "Check plane state failed (%d)\n", ret);
|
||||
@ -1002,8 +1038,8 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
|
||||
min_src_size = DPU_FORMAT_IS_YUV(fmt) ? 2 : 1;
|
||||
|
||||
if (DPU_FORMAT_IS_YUV(fmt) &&
|
||||
(!(pdpu->pipe_hw->cap->features & DPU_SSPP_SCALER) ||
|
||||
!(pdpu->pipe_hw->cap->features & DPU_SSPP_CSC_ANY))) {
|
||||
(!(pipe_hw_caps->features & DPU_SSPP_SCALER) ||
|
||||
!(pipe_hw_caps->features & DPU_SSPP_CSC_ANY))) {
|
||||
DPU_DEBUG_PLANE(pdpu,
|
||||
"plane doesn't have scaler/csc for yuv\n");
|
||||
return -EINVAL;
|
||||
@ -1036,6 +1072,22 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0;
|
||||
|
||||
if (pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION))
|
||||
supported_rotations |= DRM_MODE_ROTATE_90;
|
||||
|
||||
rotation = drm_rotation_simplify(new_plane_state->rotation,
|
||||
supported_rotations);
|
||||
|
||||
if ((pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) &&
|
||||
(rotation & DRM_MODE_ROTATE_90)) {
|
||||
ret = dpu_plane_check_inline_rotation(pdpu, sblk, src, fmt);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pstate->rotation = rotation;
|
||||
pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
|
||||
|
||||
return 0;
|
||||
@ -1151,29 +1203,27 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
|
||||
pstate->multirect_mode);
|
||||
|
||||
if (pdpu->pipe_hw->ops.setup_format) {
|
||||
unsigned int rotation;
|
||||
unsigned int rotation = pstate->rotation;
|
||||
|
||||
src_flags = 0x0;
|
||||
|
||||
rotation = drm_rotation_simplify(state->rotation,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
DRM_MODE_REFLECT_X |
|
||||
DRM_MODE_REFLECT_Y);
|
||||
|
||||
if (rotation & DRM_MODE_REFLECT_X)
|
||||
src_flags |= DPU_SSPP_FLIP_LR;
|
||||
|
||||
if (rotation & DRM_MODE_REFLECT_Y)
|
||||
src_flags |= DPU_SSPP_FLIP_UD;
|
||||
|
||||
if (rotation & DRM_MODE_ROTATE_90)
|
||||
src_flags |= DPU_SSPP_ROT_90;
|
||||
|
||||
/* update format */
|
||||
pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, fmt, src_flags,
|
||||
pstate->multirect_index);
|
||||
|
||||
if (pdpu->pipe_hw->ops.setup_cdp) {
|
||||
struct dpu_hw_pipe_cdp_cfg cdp_cfg;
|
||||
struct dpu_hw_cdp_cfg cdp_cfg;
|
||||
|
||||
memset(&cdp_cfg, 0, sizeof(struct dpu_hw_pipe_cdp_cfg));
|
||||
memset(&cdp_cfg, 0, sizeof(struct dpu_hw_cdp_cfg));
|
||||
|
||||
cdp_cfg.enable = pdpu->catalog->perf.cdp_cfg
|
||||
[DPU_PERF_CDP_USAGE_RT].rd_enable;
|
||||
@ -1411,13 +1461,9 @@ static bool dpu_plane_format_mod_supported(struct drm_plane *plane,
|
||||
if (modifier == DRM_FORMAT_MOD_LINEAR)
|
||||
return true;
|
||||
|
||||
if (modifier == DRM_FORMAT_MOD_QCOM_COMPRESSED) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(qcom_compressed_supported_formats); i++) {
|
||||
if (format == qcom_compressed_supported_formats[i])
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (modifier == DRM_FORMAT_MOD_QCOM_COMPRESSED)
|
||||
return dpu_find_format(format, qcom_compressed_supported_formats,
|
||||
ARRAY_SIZE(qcom_compressed_supported_formats));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1462,6 +1508,7 @@ struct drm_plane *dpu_plane_init(struct drm_device *dev,
|
||||
struct dpu_kms *kms = to_dpu_kms(priv->kms);
|
||||
int zpos_max = DPU_ZPOS_MAX;
|
||||
uint32_t num_formats;
|
||||
uint32_t supported_rotations;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* create and zero local structure */
|
||||
@ -1530,12 +1577,13 @@ struct drm_plane *dpu_plane_init(struct drm_device *dev,
|
||||
BIT(DRM_MODE_BLEND_PREMULTI) |
|
||||
BIT(DRM_MODE_BLEND_COVERAGE));
|
||||
|
||||
supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0;
|
||||
|
||||
if (pdpu->pipe_hw->cap->features & BIT(DPU_SSPP_INLINE_ROTATION))
|
||||
supported_rotations |= DRM_MODE_ROTATE_MASK;
|
||||
|
||||
drm_plane_create_rotation_property(plane,
|
||||
DRM_MODE_ROTATE_0,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
DRM_MODE_ROTATE_180 |
|
||||
DRM_MODE_REFLECT_X |
|
||||
DRM_MODE_REFLECT_Y);
|
||||
DRM_MODE_ROTATE_0, supported_rotations);
|
||||
|
||||
drm_plane_enable_fb_damage_clips(plane);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @plane_fetch_bw: calculated BW per plane
|
||||
* @plane_clk: calculated clk per plane
|
||||
* @needs_dirtyfb: whether attached CRTC needs pixel data explicitly flushed
|
||||
* @rotation: simplified drm rotation hint
|
||||
*/
|
||||
struct dpu_plane_state {
|
||||
struct drm_plane_state base;
|
||||
@ -40,6 +41,7 @@ struct dpu_plane_state {
|
||||
u64 plane_clk;
|
||||
|
||||
bool needs_dirtyfb;
|
||||
unsigned int rotation;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -9,8 +9,10 @@
|
||||
#include "dpu_hw_ctl.h"
|
||||
#include "dpu_hw_pingpong.h"
|
||||
#include "dpu_hw_intf.h"
|
||||
#include "dpu_hw_wb.h"
|
||||
#include "dpu_hw_dspp.h"
|
||||
#include "dpu_hw_merge3d.h"
|
||||
#include "dpu_hw_dsc.h"
|
||||
#include "dpu_encoder.h"
|
||||
#include "dpu_trace.h"
|
||||
|
||||
@ -77,6 +79,18 @@ int dpu_rm_destroy(struct dpu_rm *rm)
|
||||
for (i = 0; i < ARRAY_SIZE(rm->hw_intf); i++)
|
||||
dpu_hw_intf_destroy(rm->hw_intf[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rm->dsc_blks); i++) {
|
||||
struct dpu_hw_dsc *hw;
|
||||
|
||||
if (rm->dsc_blks[i]) {
|
||||
hw = to_dpu_hw_dsc(rm->dsc_blks[i]);
|
||||
dpu_hw_dsc_destroy(hw);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rm->hw_wb); i++)
|
||||
dpu_hw_wb_destroy(rm->hw_wb[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -176,6 +190,24 @@ int dpu_rm_init(struct dpu_rm *rm,
|
||||
rm->hw_intf[intf->id - INTF_0] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < cat->wb_count; i++) {
|
||||
struct dpu_hw_wb *hw;
|
||||
const struct dpu_wb_cfg *wb = &cat->wb[i];
|
||||
|
||||
if (wb->id < WB_0 || wb->id >= WB_MAX) {
|
||||
DPU_ERROR("skip intf %d with invalid id\n", wb->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
hw = dpu_hw_wb_init(wb->id, mmio, cat);
|
||||
if (IS_ERR(hw)) {
|
||||
rc = PTR_ERR(hw);
|
||||
DPU_ERROR("failed wb object creation: err %d\n", rc);
|
||||
goto fail;
|
||||
}
|
||||
rm->hw_wb[wb->id - WB_0] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < cat->ctl_count; i++) {
|
||||
struct dpu_hw_ctl *hw;
|
||||
const struct dpu_ctl_cfg *ctl = &cat->ctl[i];
|
||||
@ -210,6 +242,19 @@ int dpu_rm_init(struct dpu_rm *rm,
|
||||
rm->dspp_blks[dspp->id - DSPP_0] = &hw->base;
|
||||
}
|
||||
|
||||
for (i = 0; i < cat->dsc_count; i++) {
|
||||
struct dpu_hw_dsc *hw;
|
||||
const struct dpu_dsc_cfg *dsc = &cat->dsc[i];
|
||||
|
||||
hw = dpu_hw_dsc_init(dsc->id, mmio, cat);
|
||||
if (IS_ERR_OR_NULL(hw)) {
|
||||
rc = PTR_ERR(hw);
|
||||
DPU_ERROR("failed dsc object creation: err %d\n", rc);
|
||||
goto fail;
|
||||
}
|
||||
rm->dsc_blks[dsc->id - DSC_0] = &hw->base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
@ -441,6 +486,28 @@ static int _dpu_rm_reserve_ctls(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dpu_rm_reserve_dsc(struct dpu_rm *rm,
|
||||
struct dpu_global_state *global_state,
|
||||
struct drm_encoder *enc,
|
||||
const struct msm_display_topology *top)
|
||||
{
|
||||
int num_dsc = top->num_dsc;
|
||||
int i;
|
||||
|
||||
/* check if DSC required are allocated or not */
|
||||
for (i = 0; i < num_dsc; i++) {
|
||||
if (global_state->dsc_to_enc_id[i]) {
|
||||
DPU_ERROR("DSC %d is already allocated\n", i);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_dsc; i++)
|
||||
global_state->dsc_to_enc_id[i] = enc->base.id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dpu_rm_make_reservation(
|
||||
struct dpu_rm *rm,
|
||||
struct dpu_global_state *global_state,
|
||||
@ -462,6 +529,10 @@ static int _dpu_rm_make_reservation(
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _dpu_rm_reserve_dsc(rm, global_state, enc, &reqs->topology);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -499,6 +570,8 @@ void dpu_rm_release(struct dpu_global_state *global_state,
|
||||
ARRAY_SIZE(global_state->mixer_to_enc_id), enc->base.id);
|
||||
_dpu_rm_clear_mapping(global_state->ctl_to_enc_id,
|
||||
ARRAY_SIZE(global_state->ctl_to_enc_id), enc->base.id);
|
||||
_dpu_rm_clear_mapping(global_state->dsc_to_enc_id,
|
||||
ARRAY_SIZE(global_state->dsc_to_enc_id), enc->base.id);
|
||||
}
|
||||
|
||||
int dpu_rm_reserve(
|
||||
@ -567,6 +640,11 @@ int dpu_rm_get_assigned_resources(struct dpu_rm *rm,
|
||||
hw_to_enc_id = global_state->dspp_to_enc_id;
|
||||
max_blks = ARRAY_SIZE(rm->dspp_blks);
|
||||
break;
|
||||
case DPU_HW_BLK_DSC:
|
||||
hw_blks = rm->dsc_blks;
|
||||
hw_to_enc_id = global_state->dsc_to_enc_id;
|
||||
max_blks = ARRAY_SIZE(rm->dsc_blks);
|
||||
break;
|
||||
default:
|
||||
DPU_ERROR("blk type %d not managed by rm\n", type);
|
||||
return 0;
|
||||
|
@ -19,6 +19,7 @@ struct dpu_global_state;
|
||||
* @mixer_blks: array of layer mixer hardware resources
|
||||
* @ctl_blks: array of ctl hardware resources
|
||||
* @hw_intf: array of intf hardware resources
|
||||
* @hw_wb: array of wb hardware resources
|
||||
* @dspp_blks: array of dspp hardware resources
|
||||
*/
|
||||
struct dpu_rm {
|
||||
@ -26,8 +27,10 @@ struct dpu_rm {
|
||||
struct dpu_hw_blk *mixer_blks[LM_MAX - LM_0];
|
||||
struct dpu_hw_blk *ctl_blks[CTL_MAX - CTL_0];
|
||||
struct dpu_hw_intf *hw_intf[INTF_MAX - INTF_0];
|
||||
struct dpu_hw_wb *hw_wb[WB_MAX - WB_0];
|
||||
struct dpu_hw_blk *dspp_blks[DSPP_MAX - DSPP_0];
|
||||
struct dpu_hw_blk *merge_3d_blks[MERGE_3D_MAX - MERGE_3D_0];
|
||||
struct dpu_hw_blk *dsc_blks[DSC_MAX - DSC_0];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -95,5 +98,15 @@ static inline struct dpu_hw_intf *dpu_rm_get_intf(struct dpu_rm *rm, enum dpu_in
|
||||
return rm->hw_intf[intf_idx - INTF_0];
|
||||
}
|
||||
|
||||
/**
|
||||
* dpu_rm_get_wb - Return a struct dpu_hw_wb instance given it's index.
|
||||
* @rm: DPU Resource Manager handle
|
||||
* @wb_idx: WB index
|
||||
*/
|
||||
static inline struct dpu_hw_wb *dpu_rm_get_wb(struct dpu_rm *rm, enum dpu_wb wb_idx)
|
||||
{
|
||||
return rm->hw_wb[wb_idx - WB_0];
|
||||
}
|
||||
|
||||
#endif /* __DPU_RM_H__ */
|
||||
|
||||
|
@ -167,55 +167,46 @@ TRACE_EVENT(dpu_perf_crtc_update,
|
||||
__entry->update_clk)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dpu_enc_irq_template,
|
||||
TP_PROTO(uint32_t drm_id, enum dpu_intr_idx intr_idx,
|
||||
int irq_idx),
|
||||
TP_ARGS(drm_id, intr_idx, irq_idx),
|
||||
DECLARE_EVENT_CLASS(dpu_irq_template,
|
||||
TP_PROTO(int irq_idx),
|
||||
TP_ARGS(irq_idx),
|
||||
TP_STRUCT__entry(
|
||||
__field( uint32_t, drm_id )
|
||||
__field( enum dpu_intr_idx, intr_idx )
|
||||
__field( int, irq_idx )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->drm_id = drm_id;
|
||||
__entry->intr_idx = intr_idx;
|
||||
__entry->irq_idx = irq_idx;
|
||||
),
|
||||
TP_printk("id=%u, intr=%d, irq=%d",
|
||||
__entry->drm_id, __entry->intr_idx,
|
||||
__entry->irq_idx)
|
||||
TP_printk("irq=%d", __entry->irq_idx)
|
||||
);
|
||||
DEFINE_EVENT(dpu_enc_irq_template, dpu_enc_irq_register_success,
|
||||
TP_PROTO(uint32_t drm_id, enum dpu_intr_idx intr_idx,
|
||||
int irq_idx),
|
||||
TP_ARGS(drm_id, intr_idx, irq_idx)
|
||||
DEFINE_EVENT(dpu_irq_template, dpu_irq_register_success,
|
||||
TP_PROTO(int irq_idx),
|
||||
TP_ARGS(irq_idx)
|
||||
);
|
||||
DEFINE_EVENT(dpu_enc_irq_template, dpu_enc_irq_unregister_success,
|
||||
TP_PROTO(uint32_t drm_id, enum dpu_intr_idx intr_idx,
|
||||
int irq_idx),
|
||||
TP_ARGS(drm_id, intr_idx, irq_idx)
|
||||
DEFINE_EVENT(dpu_irq_template, dpu_irq_unregister_success,
|
||||
TP_PROTO(int irq_idx),
|
||||
TP_ARGS(irq_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(dpu_enc_irq_wait_success,
|
||||
TP_PROTO(uint32_t drm_id, enum dpu_intr_idx intr_idx,
|
||||
TP_PROTO(uint32_t drm_id, void *func,
|
||||
int irq_idx, enum dpu_pingpong pp_idx, int atomic_cnt),
|
||||
TP_ARGS(drm_id, intr_idx, irq_idx, pp_idx, atomic_cnt),
|
||||
TP_ARGS(drm_id, func, irq_idx, pp_idx, atomic_cnt),
|
||||
TP_STRUCT__entry(
|
||||
__field( uint32_t, drm_id )
|
||||
__field( enum dpu_intr_idx, intr_idx )
|
||||
__field( void *, func )
|
||||
__field( int, irq_idx )
|
||||
__field( enum dpu_pingpong, pp_idx )
|
||||
__field( int, atomic_cnt )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->drm_id = drm_id;
|
||||
__entry->intr_idx = intr_idx;
|
||||
__entry->func = func;
|
||||
__entry->irq_idx = irq_idx;
|
||||
__entry->pp_idx = pp_idx;
|
||||
__entry->atomic_cnt = atomic_cnt;
|
||||
),
|
||||
TP_printk("id=%u, intr=%d, irq=%d, pp=%d, atomic_cnt=%d",
|
||||
__entry->drm_id, __entry->intr_idx,
|
||||
TP_printk("id=%u, callback=%ps, irq=%d, pp=%d, atomic_cnt=%d",
|
||||
__entry->drm_id, __entry->func,
|
||||
__entry->irq_idx, __entry->pp_idx, __entry->atomic_cnt)
|
||||
);
|
||||
|
||||
@ -389,20 +380,26 @@ TRACE_EVENT(dpu_enc_rc,
|
||||
);
|
||||
|
||||
TRACE_EVENT(dpu_enc_frame_done_cb_not_busy,
|
||||
TP_PROTO(uint32_t drm_id, u32 event, enum dpu_intf intf_idx),
|
||||
TP_ARGS(drm_id, event, intf_idx),
|
||||
TP_PROTO(uint32_t drm_id, u32 event, char *intf_mode, enum dpu_intf intf_idx,
|
||||
enum dpu_wb wb_idx),
|
||||
TP_ARGS(drm_id, event, intf_mode, intf_idx, wb_idx),
|
||||
TP_STRUCT__entry(
|
||||
__field( uint32_t, drm_id )
|
||||
__field( u32, event )
|
||||
__string( intf_mode_str, intf_mode )
|
||||
__field( enum dpu_intf, intf_idx )
|
||||
__field( enum dpu_wb, wb_idx )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->drm_id = drm_id;
|
||||
__entry->event = event;
|
||||
__assign_str(intf_mode_str, intf_mode);
|
||||
__entry->intf_idx = intf_idx;
|
||||
__entry->wb_idx = wb_idx;
|
||||
),
|
||||
TP_printk("id=%u, event=%u, intf=%d", __entry->drm_id, __entry->event,
|
||||
__entry->intf_idx)
|
||||
TP_printk("id=%u, event=%u, intf_mode=%s intf=%d wb=%d", __entry->drm_id,
|
||||
__entry->event, __get_str(intf_mode_str),
|
||||
__entry->intf_idx, __entry->wb_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(dpu_enc_frame_done_cb,
|
||||
@ -424,14 +421,16 @@ TRACE_EVENT(dpu_enc_frame_done_cb,
|
||||
);
|
||||
|
||||
TRACE_EVENT(dpu_enc_trigger_flush,
|
||||
TP_PROTO(uint32_t drm_id, enum dpu_intf intf_idx,
|
||||
TP_PROTO(uint32_t drm_id, char *intf_mode, enum dpu_intf intf_idx, enum dpu_wb wb_idx,
|
||||
int pending_kickoff_cnt, int ctl_idx, u32 extra_flush_bits,
|
||||
u32 pending_flush_ret),
|
||||
TP_ARGS(drm_id, intf_idx, pending_kickoff_cnt, ctl_idx,
|
||||
TP_ARGS(drm_id, intf_mode, intf_idx, wb_idx, pending_kickoff_cnt, ctl_idx,
|
||||
extra_flush_bits, pending_flush_ret),
|
||||
TP_STRUCT__entry(
|
||||
__field( uint32_t, drm_id )
|
||||
__string( intf_mode_str, intf_mode )
|
||||
__field( enum dpu_intf, intf_idx )
|
||||
__field( enum dpu_wb, wb_idx )
|
||||
__field( int, pending_kickoff_cnt )
|
||||
__field( int, ctl_idx )
|
||||
__field( u32, extra_flush_bits )
|
||||
@ -439,15 +438,17 @@ TRACE_EVENT(dpu_enc_trigger_flush,
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->drm_id = drm_id;
|
||||
__assign_str(intf_mode_str, intf_mode);
|
||||
__entry->intf_idx = intf_idx;
|
||||
__entry->wb_idx = wb_idx;
|
||||
__entry->pending_kickoff_cnt = pending_kickoff_cnt;
|
||||
__entry->ctl_idx = ctl_idx;
|
||||
__entry->extra_flush_bits = extra_flush_bits;
|
||||
__entry->pending_flush_ret = pending_flush_ret;
|
||||
),
|
||||
TP_printk("id=%u, intf_idx=%d, pending_kickoff_cnt=%d ctl_idx=%d "
|
||||
TP_printk("id=%u, intf_mode=%s, intf_idx=%d, wb_idx=%d, pending_kickoff_cnt=%d ctl_idx=%d "
|
||||
"extra_flush_bits=0x%x pending_flush_ret=0x%x",
|
||||
__entry->drm_id, __entry->intf_idx,
|
||||
__entry->drm_id, __get_str(intf_mode_str), __entry->intf_idx, __entry->wb_idx,
|
||||
__entry->pending_kickoff_cnt, __entry->ctl_idx,
|
||||
__entry->extra_flush_bits, __entry->pending_flush_ret)
|
||||
);
|
||||
@ -871,27 +872,31 @@ TRACE_EVENT(dpu_pp_connect_ext_te,
|
||||
TP_printk("pp:%d cfg:%u", __entry->pp, __entry->cfg)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dpu_core_irq_callback_template,
|
||||
TP_PROTO(int irq_idx, struct dpu_irq_callback *callback),
|
||||
TRACE_EVENT(dpu_core_irq_register_callback,
|
||||
TP_PROTO(int irq_idx, void *callback),
|
||||
TP_ARGS(irq_idx, callback),
|
||||
TP_STRUCT__entry(
|
||||
__field( int, irq_idx )
|
||||
__field( struct dpu_irq_callback *, callback)
|
||||
__field( void *, callback)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->irq_idx = irq_idx;
|
||||
__entry->callback = callback;
|
||||
),
|
||||
TP_printk("irq_idx:%d callback:%pK", __entry->irq_idx,
|
||||
TP_printk("irq_idx:%d callback:%ps", __entry->irq_idx,
|
||||
__entry->callback)
|
||||
);
|
||||
DEFINE_EVENT(dpu_core_irq_callback_template, dpu_core_irq_register_callback,
|
||||
TP_PROTO(int irq_idx, struct dpu_irq_callback *callback),
|
||||
TP_ARGS(irq_idx, callback)
|
||||
);
|
||||
DEFINE_EVENT(dpu_core_irq_callback_template, dpu_core_irq_unregister_callback,
|
||||
TP_PROTO(int irq_idx, struct dpu_irq_callback *callback),
|
||||
TP_ARGS(irq_idx, callback)
|
||||
|
||||
TRACE_EVENT(dpu_core_irq_unregister_callback,
|
||||
TP_PROTO(int irq_idx),
|
||||
TP_ARGS(irq_idx),
|
||||
TP_STRUCT__entry(
|
||||
__field( int, irq_idx )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->irq_idx = irq_idx;
|
||||
),
|
||||
TP_printk("irq_idx:%d", __entry->irq_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(dpu_core_perf_update_clk,
|
||||
|
76
drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
Normal file
76
drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
Normal file
@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dpu_writeback.h"
|
||||
|
||||
static int dpu_wb_conn_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
|
||||
return drm_add_modes_noedid(connector, dev->mode_config.max_width,
|
||||
dev->mode_config.max_height);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dpu_wb_conn_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
|
||||
struct dpu_wb_connector *dpu_wb_conn = to_dpu_wb_conn(connector);
|
||||
|
||||
if (!job->fb)
|
||||
return 0;
|
||||
|
||||
dpu_encoder_prepare_wb_job(dpu_wb_conn->wb_enc, job);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct dpu_wb_connector *dpu_wb_conn = to_dpu_wb_conn(connector);
|
||||
|
||||
if (!job->fb)
|
||||
return;
|
||||
|
||||
dpu_encoder_cleanup_wb_job(dpu_wb_conn->wb_enc, job);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = {
|
||||
.get_modes = dpu_wb_conn_get_modes,
|
||||
.prepare_writeback_job = dpu_wb_conn_prepare_job,
|
||||
.cleanup_writeback_job = dpu_wb_conn_cleanup_job,
|
||||
};
|
||||
|
||||
int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
|
||||
const u32 *format_list, u32 num_formats)
|
||||
{
|
||||
struct dpu_wb_connector *dpu_wb_conn;
|
||||
int rc = 0;
|
||||
|
||||
dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL);
|
||||
|
||||
drm_connector_helper_add(&dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs);
|
||||
|
||||
/* DPU initializes the encoder and sets it up completely for writeback
|
||||
* cases and hence should use the new API drm_writeback_connector_init_with_encoder
|
||||
* to initialize the writeback connector
|
||||
*/
|
||||
rc = drm_writeback_connector_init_with_encoder(dev, &dpu_wb_conn->base, enc,
|
||||
&dpu_wb_conn_funcs, format_list, num_formats);
|
||||
|
||||
if (!rc)
|
||||
dpu_wb_conn->wb_enc = enc;
|
||||
|
||||
return rc;
|
||||
}
|
31
drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
Normal file
31
drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DPU_WRITEBACK_H
|
||||
#define _DPU_WRITEBACK_H
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_writeback.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "dpu_kms.h"
|
||||
#include "dpu_encoder_phys.h"
|
||||
|
||||
struct dpu_wb_connector {
|
||||
struct drm_writeback_connector base;
|
||||
struct drm_encoder *wb_enc;
|
||||
};
|
||||
|
||||
static inline struct dpu_wb_connector *to_dpu_wb_conn(struct drm_writeback_connector *conn)
|
||||
{
|
||||
return container_of(conn, struct dpu_wb_connector, base);
|
||||
}
|
||||
|
||||
int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
|
||||
const u32 *format_list, u32 num_formats);
|
||||
|
||||
#endif /*_DPU_WRITEBACK_H */
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_DSI
|
||||
|
||||
struct mdp4_dsi_encoder {
|
||||
struct drm_encoder base;
|
||||
struct drm_panel *panel;
|
||||
@ -170,3 +172,4 @@ fail:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
#endif /* CONFIG_DRM_MSM_DSI */
|
||||
|
@ -21,7 +21,6 @@ static int mdp4_hw_init(struct msm_kms *kms)
|
||||
struct drm_device *dev = mdp4_kms->dev;
|
||||
u32 dmap_cfg, vg_cfg;
|
||||
unsigned long clk;
|
||||
int ret = 0;
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
@ -72,7 +71,7 @@ static int mdp4_hw_init(struct msm_kms *kms)
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdp4_enable_commit(struct msm_kms *kms)
|
||||
@ -229,9 +228,6 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
|
||||
return PTR_ERR(connector);
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
priv->connectors[priv->num_connectors++] = connector;
|
||||
|
||||
break;
|
||||
case DRM_MODE_ENCODER_TMDS:
|
||||
encoder = mdp4_dtv_encoder_init(dev);
|
||||
@ -252,8 +248,6 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
|
||||
}
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
break;
|
||||
case DRM_MODE_ENCODER_DSI:
|
||||
/* only DSI1 supported for now */
|
||||
@ -272,7 +266,6 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
|
||||
|
||||
/* TODO: Add DMA_S later? */
|
||||
encoder->possible_crtcs = 1 << DMA_P;
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, encoder);
|
||||
if (ret) {
|
||||
@ -324,7 +317,6 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
|
||||
ret = PTR_ERR(plane);
|
||||
goto fail;
|
||||
}
|
||||
priv->planes[priv->num_planes++] = plane;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mdp4_crtcs); i++) {
|
||||
@ -389,7 +381,7 @@ static void read_mdp_hw_revision(struct mdp4_kms *mdp4_kms,
|
||||
DRM_DEV_INFO(dev->dev, "MDP4 version v%d.%d", *major, *minor);
|
||||
}
|
||||
|
||||
struct msm_kms *mdp4_kms_init(struct drm_device *dev)
|
||||
static int mdp4_kms_init(struct drm_device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev->dev);
|
||||
struct mdp4_platform_config *config = mdp4_get_config(pdev);
|
||||
@ -403,8 +395,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
|
||||
mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
|
||||
if (!mdp4_kms) {
|
||||
DRM_DEV_ERROR(dev->dev, "failed to allocate kms\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = mdp_kms_init(&mdp4_kms->base, &kms_funcs);
|
||||
@ -551,12 +542,13 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
|
||||
return kms;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (kms)
|
||||
mdp4_destroy(kms);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
|
||||
@ -569,3 +561,47 @@ static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
|
||||
|
||||
return &config;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mdp4_pm_ops = {
|
||||
.prepare = msm_pm_prepare,
|
||||
.complete = msm_pm_complete,
|
||||
};
|
||||
|
||||
static int mdp4_probe(struct platform_device *pdev)
|
||||
{
|
||||
return msm_drv_probe(&pdev->dev, mdp4_kms_init);
|
||||
}
|
||||
|
||||
static int mdp4_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_master_del(&pdev->dev, &msm_drm_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mdp4_dt_match[] = {
|
||||
{ .compatible = "qcom,mdp4" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mdp4_dt_match);
|
||||
|
||||
static struct platform_driver mdp4_platform_driver = {
|
||||
.probe = mdp4_probe,
|
||||
.remove = mdp4_remove,
|
||||
.shutdown = msm_drv_shutdown,
|
||||
.driver = {
|
||||
.name = "mdp4",
|
||||
.of_match_table = mdp4_dt_match,
|
||||
.pm = &mdp4_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
void __init msm_mdp4_register(void)
|
||||
{
|
||||
platform_driver_register(&mdp4_platform_driver);
|
||||
}
|
||||
|
||||
void __exit msm_mdp4_unregister(void)
|
||||
{
|
||||
platform_driver_unregister(&mdp4_platform_driver);
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "mdp5_kms.h"
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_DSI
|
||||
|
||||
static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
|
||||
{
|
||||
struct msm_drm_private *priv = encoder->dev->dev_private;
|
||||
@ -198,3 +200,4 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DRM_MSM_DSI */
|
||||
|
@ -612,9 +612,15 @@ static int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mdp5_mixer_release(new_crtc_state->state, old_mixer);
|
||||
ret = mdp5_mixer_release(new_crtc_state->state, old_mixer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (old_r_mixer) {
|
||||
mdp5_mixer_release(new_crtc_state->state, old_r_mixer);
|
||||
ret = mdp5_mixer_release(new_crtc_state->state, old_r_mixer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!need_right_mixer)
|
||||
pipeline->r_mixer = NULL;
|
||||
}
|
||||
|
@ -203,6 +203,8 @@ static int mdp5_set_split_display(struct msm_kms *kms,
|
||||
slave_encoder);
|
||||
}
|
||||
|
||||
static void mdp5_destroy(struct platform_device *pdev);
|
||||
|
||||
static void mdp5_kms_destroy(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
||||
@ -221,6 +223,7 @@ static void mdp5_kms_destroy(struct msm_kms *kms)
|
||||
}
|
||||
|
||||
mdp_kms_destroy(&mdp5_kms->base);
|
||||
mdp5_destroy(mdp5_kms->pdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
@ -319,7 +322,6 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
|
||||
struct mdp5_ctl *ctl)
|
||||
{
|
||||
struct drm_device *dev = mdp5_kms->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
encoder = mdp5_encoder_init(dev, intf, ctl);
|
||||
@ -328,8 +330,6 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
|
||||
return encoder;
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
@ -434,6 +434,8 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
int i, ret, pi = 0, ci = 0;
|
||||
struct drm_plane *primary[MAX_BASES] = { NULL };
|
||||
struct drm_plane *cursor[MAX_BASES] = { NULL };
|
||||
struct drm_encoder *encoder;
|
||||
unsigned int num_encoders;
|
||||
|
||||
/*
|
||||
* Construct encoders and modeset initialize connector devices
|
||||
@ -445,12 +447,16 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
num_encoders = 0;
|
||||
drm_for_each_encoder(encoder, dev)
|
||||
num_encoders++;
|
||||
|
||||
/*
|
||||
* We should ideally have less number of encoders (set up by parsing
|
||||
* the MDP5 interfaces) than the number of layer mixers present in HW,
|
||||
* but let's be safe here anyway
|
||||
*/
|
||||
num_crtcs = min(priv->num_encoders, mdp5_kms->num_hwmixers);
|
||||
num_crtcs = min(num_encoders, mdp5_kms->num_hwmixers);
|
||||
|
||||
/*
|
||||
* Construct planes equaling the number of hw pipes, and CRTCs for the
|
||||
@ -475,7 +481,6 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
DRM_DEV_ERROR(dev->dev, "failed to construct plane %d (%d)\n", i, ret);
|
||||
goto fail;
|
||||
}
|
||||
priv->planes[priv->num_planes++] = plane;
|
||||
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY)
|
||||
primary[pi++] = plane;
|
||||
@ -499,11 +504,8 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
* Now that we know the number of crtcs we've created, set the possible
|
||||
* crtcs for the encoders
|
||||
*/
|
||||
for (i = 0; i < priv->num_encoders; i++) {
|
||||
struct drm_encoder *encoder = priv->encoders[i];
|
||||
|
||||
drm_for_each_encoder(encoder, dev)
|
||||
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -544,7 +546,9 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
static int mdp5_init(struct platform_device *pdev, struct drm_device *dev);
|
||||
|
||||
static int mdp5_kms_init(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct platform_device *pdev;
|
||||
@ -555,10 +559,12 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
int irq, i, ret;
|
||||
struct device *iommu_dev;
|
||||
|
||||
ret = mdp5_init(to_platform_device(dev->dev), dev);
|
||||
|
||||
/* priv->kms would have been populated by the MDP5 driver */
|
||||
kms = priv->kms;
|
||||
if (!kms)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
||||
pdev = mdp5_kms->pdev;
|
||||
@ -570,9 +576,9 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get irq: %d\n", ret);
|
||||
if (!irq) {
|
||||
ret = -EINVAL;
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get irq\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -637,11 +643,12 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
dev->max_vblank_count = 0; /* max_vblank_count is set on each CRTC */
|
||||
dev->vblank_disable_immediate = true;
|
||||
|
||||
return kms;
|
||||
return 0;
|
||||
fail:
|
||||
if (kms)
|
||||
mdp5_kms_destroy(kms);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mdp5_destroy(struct platform_device *pdev)
|
||||
@ -912,35 +919,14 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mdp5_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(master);
|
||||
struct drm_device *ddev = priv->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
DBG("");
|
||||
|
||||
return mdp5_init(pdev, ddev);
|
||||
}
|
||||
|
||||
static void mdp5_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
mdp5_destroy(pdev);
|
||||
}
|
||||
|
||||
static const struct component_ops mdp5_ops = {
|
||||
.bind = mdp5_bind,
|
||||
.unbind = mdp5_unbind,
|
||||
};
|
||||
|
||||
static int mdp5_setup_interconnect(struct platform_device *pdev)
|
||||
{
|
||||
struct icc_path *path0 = of_icc_get(&pdev->dev, "mdp0-mem");
|
||||
struct icc_path *path1 = of_icc_get(&pdev->dev, "mdp1-mem");
|
||||
struct icc_path *path_rot = of_icc_get(&pdev->dev, "rotator-mem");
|
||||
/* Interconnects are a part of MDSS device tree binding, not the
|
||||
* MDP5 device. */
|
||||
struct device *mdss_dev = pdev->dev.parent;
|
||||
struct icc_path *path0 = of_icc_get(mdss_dev, "mdp0-mem");
|
||||
struct icc_path *path1 = of_icc_get(mdss_dev, "mdp1-mem");
|
||||
struct icc_path *path_rot = of_icc_get(mdss_dev, "rotator-mem");
|
||||
|
||||
if (IS_ERR(path0))
|
||||
return PTR_ERR(path0);
|
||||
@ -976,13 +962,13 @@ static int mdp5_dev_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return component_add(&pdev->dev, &mdp5_ops);
|
||||
return msm_drv_probe(&pdev->dev, mdp5_kms_init);
|
||||
}
|
||||
|
||||
static int mdp5_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
DBG("");
|
||||
component_del(&pdev->dev, &mdp5_ops);
|
||||
component_master_del(&pdev->dev, &msm_drm_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1008,9 +994,11 @@ static __maybe_unused int mdp5_runtime_resume(struct device *dev)
|
||||
|
||||
static const struct dev_pm_ops mdp5_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mdp5_runtime_suspend, mdp5_runtime_resume, NULL)
|
||||
.prepare = msm_pm_prepare,
|
||||
.complete = msm_pm_complete,
|
||||
};
|
||||
|
||||
const struct of_device_id mdp5_dt_match[] = {
|
||||
static const struct of_device_id mdp5_dt_match[] = {
|
||||
{ .compatible = "qcom,mdp5", },
|
||||
/* to support downstream DT files */
|
||||
{ .compatible = "qcom,mdss_mdp", },
|
||||
@ -1021,6 +1009,7 @@ MODULE_DEVICE_TABLE(of, mdp5_dt_match);
|
||||
static struct platform_driver mdp5_driver = {
|
||||
.probe = mdp5_dev_probe,
|
||||
.remove = mdp5_dev_remove,
|
||||
.shutdown = msm_drv_shutdown,
|
||||
.driver = {
|
||||
.name = "msm_mdp",
|
||||
.of_match_table = mdp5_dt_match,
|
||||
|
@ -1,252 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "mdp5_kms.h"
|
||||
|
||||
#define to_mdp5_mdss(x) container_of(x, struct mdp5_mdss, base)
|
||||
|
||||
struct mdp5_mdss {
|
||||
struct msm_mdss base;
|
||||
|
||||
void __iomem *mmio, *vbif;
|
||||
|
||||
struct clk *ahb_clk;
|
||||
struct clk *axi_clk;
|
||||
struct clk *vsync_clk;
|
||||
|
||||
struct {
|
||||
volatile unsigned long enabled_mask;
|
||||
struct irq_domain *domain;
|
||||
} irqcontroller;
|
||||
};
|
||||
|
||||
static inline void mdss_write(struct mdp5_mdss *mdp5_mdss, u32 reg, u32 data)
|
||||
{
|
||||
msm_writel(data, mdp5_mdss->mmio + reg);
|
||||
}
|
||||
|
||||
static inline u32 mdss_read(struct mdp5_mdss *mdp5_mdss, u32 reg)
|
||||
{
|
||||
return msm_readl(mdp5_mdss->mmio + reg);
|
||||
}
|
||||
|
||||
static irqreturn_t mdss_irq(int irq, void *arg)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = arg;
|
||||
u32 intr;
|
||||
|
||||
intr = mdss_read(mdp5_mdss, REG_MDSS_HW_INTR_STATUS);
|
||||
|
||||
VERB("intr=%08x", intr);
|
||||
|
||||
while (intr) {
|
||||
irq_hw_number_t hwirq = fls(intr) - 1;
|
||||
|
||||
generic_handle_domain_irq(mdp5_mdss->irqcontroller.domain, hwirq);
|
||||
intr &= ~(1 << hwirq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt-controller implementation, so sub-blocks (MDP/HDMI/eDP/DSI/etc)
|
||||
* can register to get their irq's delivered
|
||||
*/
|
||||
|
||||
#define VALID_IRQS (MDSS_HW_INTR_STATUS_INTR_MDP | \
|
||||
MDSS_HW_INTR_STATUS_INTR_DSI0 | \
|
||||
MDSS_HW_INTR_STATUS_INTR_DSI1 | \
|
||||
MDSS_HW_INTR_STATUS_INTR_HDMI | \
|
||||
MDSS_HW_INTR_STATUS_INTR_EDP)
|
||||
|
||||
static void mdss_hw_mask_irq(struct irq_data *irqd)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = irq_data_get_irq_chip_data(irqd);
|
||||
|
||||
smp_mb__before_atomic();
|
||||
clear_bit(irqd->hwirq, &mdp5_mdss->irqcontroller.enabled_mask);
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
|
||||
static void mdss_hw_unmask_irq(struct irq_data *irqd)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = irq_data_get_irq_chip_data(irqd);
|
||||
|
||||
smp_mb__before_atomic();
|
||||
set_bit(irqd->hwirq, &mdp5_mdss->irqcontroller.enabled_mask);
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
|
||||
static struct irq_chip mdss_hw_irq_chip = {
|
||||
.name = "mdss",
|
||||
.irq_mask = mdss_hw_mask_irq,
|
||||
.irq_unmask = mdss_hw_unmask_irq,
|
||||
};
|
||||
|
||||
static int mdss_hw_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = d->host_data;
|
||||
|
||||
if (!(VALID_IRQS & (1 << hwirq)))
|
||||
return -EPERM;
|
||||
|
||||
irq_set_chip_and_handler(irq, &mdss_hw_irq_chip, handle_level_irq);
|
||||
irq_set_chip_data(irq, mdp5_mdss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops mdss_hw_irqdomain_ops = {
|
||||
.map = mdss_hw_irqdomain_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
|
||||
static int mdss_irq_domain_init(struct mdp5_mdss *mdp5_mdss)
|
||||
{
|
||||
struct device *dev = mdp5_mdss->base.dev;
|
||||
struct irq_domain *d;
|
||||
|
||||
d = irq_domain_add_linear(dev->of_node, 32, &mdss_hw_irqdomain_ops,
|
||||
mdp5_mdss);
|
||||
if (!d) {
|
||||
DRM_DEV_ERROR(dev, "mdss irq domain add failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mdp5_mdss->irqcontroller.enabled_mask = 0;
|
||||
mdp5_mdss->irqcontroller.domain = d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdp5_mdss_enable(struct msm_mdss *mdss)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
||||
DBG("");
|
||||
|
||||
clk_prepare_enable(mdp5_mdss->ahb_clk);
|
||||
clk_prepare_enable(mdp5_mdss->axi_clk);
|
||||
clk_prepare_enable(mdp5_mdss->vsync_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdp5_mdss_disable(struct msm_mdss *mdss)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
||||
DBG("");
|
||||
|
||||
clk_disable_unprepare(mdp5_mdss->vsync_clk);
|
||||
clk_disable_unprepare(mdp5_mdss->axi_clk);
|
||||
clk_disable_unprepare(mdp5_mdss->ahb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_mdss_get_clocks(struct mdp5_mdss *mdp5_mdss)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
to_platform_device(mdp5_mdss->base.dev);
|
||||
|
||||
mdp5_mdss->ahb_clk = msm_clk_get(pdev, "iface");
|
||||
if (IS_ERR(mdp5_mdss->ahb_clk))
|
||||
mdp5_mdss->ahb_clk = NULL;
|
||||
|
||||
mdp5_mdss->axi_clk = msm_clk_get(pdev, "bus");
|
||||
if (IS_ERR(mdp5_mdss->axi_clk))
|
||||
mdp5_mdss->axi_clk = NULL;
|
||||
|
||||
mdp5_mdss->vsync_clk = msm_clk_get(pdev, "vsync");
|
||||
if (IS_ERR(mdp5_mdss->vsync_clk))
|
||||
mdp5_mdss->vsync_clk = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdp5_mdss_destroy(struct msm_mdss *mdss)
|
||||
{
|
||||
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
||||
|
||||
if (!mdp5_mdss)
|
||||
return;
|
||||
|
||||
irq_domain_remove(mdp5_mdss->irqcontroller.domain);
|
||||
mdp5_mdss->irqcontroller.domain = NULL;
|
||||
|
||||
pm_runtime_disable(mdss->dev);
|
||||
}
|
||||
|
||||
static const struct msm_mdss_funcs mdss_funcs = {
|
||||
.enable = mdp5_mdss_enable,
|
||||
.disable = mdp5_mdss_disable,
|
||||
.destroy = mdp5_mdss_destroy,
|
||||
};
|
||||
|
||||
int mdp5_mdss_init(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct mdp5_mdss *mdp5_mdss;
|
||||
int ret;
|
||||
|
||||
DBG("");
|
||||
|
||||
if (!of_device_is_compatible(pdev->dev.of_node, "qcom,mdss"))
|
||||
return 0;
|
||||
|
||||
mdp5_mdss = devm_kzalloc(&pdev->dev, sizeof(*mdp5_mdss), GFP_KERNEL);
|
||||
if (!mdp5_mdss) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp5_mdss->base.dev = &pdev->dev;
|
||||
|
||||
mdp5_mdss->mmio = msm_ioremap(pdev, "mdss_phys");
|
||||
if (IS_ERR(mdp5_mdss->mmio)) {
|
||||
ret = PTR_ERR(mdp5_mdss->mmio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp5_mdss->vbif = msm_ioremap(pdev, "vbif_phys");
|
||||
if (IS_ERR(mdp5_mdss->vbif)) {
|
||||
ret = PTR_ERR(mdp5_mdss->vbif);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = msm_mdss_get_clocks(mdp5_mdss);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get clocks: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0),
|
||||
mdss_irq, 0, "mdss_isr", mdp5_mdss);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to init irq: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = mdss_irq_domain_init(mdp5_mdss);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to init sub-block irqs: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp5_mdss->base.funcs = &mdss_funcs;
|
||||
priv->mdss = &mdp5_mdss->base;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
@ -116,21 +116,28 @@ int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
|
||||
int mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
|
||||
{
|
||||
struct mdp5_global_state *global_state = mdp5_get_global_state(s);
|
||||
struct mdp5_hw_mixer_state *new_state = &global_state->hwmixer;
|
||||
struct mdp5_hw_mixer_state *new_state;
|
||||
|
||||
if (!mixer)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (IS_ERR(global_state))
|
||||
return PTR_ERR(global_state);
|
||||
|
||||
new_state = &global_state->hwmixer;
|
||||
|
||||
if (WARN_ON(!new_state->hwmixer_to_crtc[mixer->idx]))
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
DBG("%s: release from crtc %s", mixer->name,
|
||||
new_state->hwmixer_to_crtc[mixer->idx]->name);
|
||||
|
||||
new_state->hwmixer_to_crtc[mixer->idx] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer)
|
||||
|
@ -30,7 +30,7 @@ void mdp5_mixer_destroy(struct mdp5_hw_mixer *lm);
|
||||
int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc,
|
||||
uint32_t caps, struct mdp5_hw_mixer **mixer,
|
||||
struct mdp5_hw_mixer **r_mixer);
|
||||
void mdp5_mixer_release(struct drm_atomic_state *s,
|
||||
struct mdp5_hw_mixer *mixer);
|
||||
int mdp5_mixer_release(struct drm_atomic_state *s,
|
||||
struct mdp5_hw_mixer *mixer);
|
||||
|
||||
#endif /* __MDP5_LM_H__ */
|
||||
|
@ -119,18 +119,23 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
|
||||
int mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
|
||||
{
|
||||
struct msm_drm_private *priv = s->dev->dev_private;
|
||||
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
|
||||
struct mdp5_global_state *state = mdp5_get_global_state(s);
|
||||
struct mdp5_hw_pipe_state *new_state = &state->hwpipe;
|
||||
struct mdp5_hw_pipe_state *new_state;
|
||||
|
||||
if (!hwpipe)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (IS_ERR(state))
|
||||
return PTR_ERR(state);
|
||||
|
||||
new_state = &state->hwpipe;
|
||||
|
||||
if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
DBG("%s: release from plane %s", hwpipe->name,
|
||||
new_state->hwpipe_to_plane[hwpipe->idx]->name);
|
||||
@ -141,6 +146,8 @@ void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
|
||||
}
|
||||
|
||||
new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
|
||||
|
@ -37,7 +37,7 @@ int mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
|
||||
uint32_t caps, uint32_t blkcfg,
|
||||
struct mdp5_hw_pipe **hwpipe,
|
||||
struct mdp5_hw_pipe **r_hwpipe);
|
||||
void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe);
|
||||
int mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe);
|
||||
|
||||
struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
|
||||
uint32_t reg_offset, uint32_t caps);
|
||||
|
@ -314,12 +314,24 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,
|
||||
mdp5_state->r_hwpipe = NULL;
|
||||
|
||||
|
||||
mdp5_pipe_release(state->state, old_hwpipe);
|
||||
mdp5_pipe_release(state->state, old_right_hwpipe);
|
||||
ret = mdp5_pipe_release(state->state, old_hwpipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mdp5_pipe_release(state->state, old_right_hwpipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
} else {
|
||||
mdp5_pipe_release(state->state, mdp5_state->hwpipe);
|
||||
mdp5_pipe_release(state->state, mdp5_state->r_hwpipe);
|
||||
ret = mdp5_pipe_release(state->state, mdp5_state->hwpipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mdp5_pipe_release(state->state, mdp5_state->r_hwpipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mdp5_state->hwpipe = mdp5_state->r_hwpipe = NULL;
|
||||
}
|
||||
|
||||
@ -385,8 +397,6 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
|
||||
if (!crtc_state->active)
|
||||
return -EINVAL;
|
||||
|
||||
mdp5_state = to_mdp5_plane_state(new_plane_state);
|
||||
|
||||
/* don't use fast path if we don't have a hwpipe allocated yet */
|
||||
if (!mdp5_state->hwpipe)
|
||||
return -EINVAL;
|
||||
|
@ -26,6 +26,7 @@
|
||||
struct dp_audio_private {
|
||||
struct platform_device *audio_pdev;
|
||||
struct platform_device *pdev;
|
||||
struct drm_device *drm_dev;
|
||||
struct dp_catalog *catalog;
|
||||
struct dp_panel *panel;
|
||||
|
||||
@ -136,7 +137,8 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_1_BIT)
|
||||
| (parity_byte << PARITY_BYTE_1_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
|
||||
@ -148,7 +150,8 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_2_BIT)
|
||||
| (parity_byte << PARITY_BYTE_2_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
|
||||
dp_audio_set_header(catalog, value,
|
||||
@ -162,7 +165,8 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_3_BIT)
|
||||
| (parity_byte << PARITY_BYTE_3_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
|
||||
dp_audio_set_header(catalog, value,
|
||||
@ -183,8 +187,9 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_1_BIT)
|
||||
| (parity_byte << PARITY_BYTE_1_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
|
||||
|
||||
@ -196,7 +201,8 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_2_BIT)
|
||||
| (parity_byte << PARITY_BYTE_2_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
|
||||
@ -209,7 +215,8 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_3_BIT)
|
||||
| (parity_byte << PARITY_BYTE_3_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
|
||||
@ -229,7 +236,8 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_1_BIT)
|
||||
| (parity_byte << PARITY_BYTE_1_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
|
||||
@ -242,7 +250,8 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_2_BIT)
|
||||
| (parity_byte << PARITY_BYTE_2_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
|
||||
@ -255,7 +264,8 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_3_BIT)
|
||||
| (parity_byte << PARITY_BYTE_3_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
new_value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
|
||||
@ -275,7 +285,8 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_1_BIT)
|
||||
| (parity_byte << PARITY_BYTE_1_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
|
||||
@ -288,7 +299,8 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_2_BIT)
|
||||
| (parity_byte << PARITY_BYTE_2_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
|
||||
@ -301,7 +313,8 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_3_BIT)
|
||||
| (parity_byte << PARITY_BYTE_3_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
|
||||
@ -321,7 +334,8 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_1_BIT)
|
||||
| (parity_byte << PARITY_BYTE_1_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
|
||||
@ -334,7 +348,8 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
|
||||
parity_byte = dp_audio_calculate_parity(new_value);
|
||||
value |= ((new_value << HEADER_BYTE_2_BIT)
|
||||
| (parity_byte << PARITY_BYTE_2_BIT));
|
||||
DRM_DEBUG_DP("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
|
||||
value, parity_byte);
|
||||
dp_audio_set_header(catalog, value,
|
||||
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
|
||||
@ -370,7 +385,7 @@ static void dp_audio_setup_acr(struct dp_audio_private *audio)
|
||||
select = 3;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DP("Unknown link rate\n");
|
||||
drm_dbg_dp(audio->drm_dev, "Unknown link rate\n");
|
||||
select = 0;
|
||||
break;
|
||||
}
|
||||
@ -395,7 +410,8 @@ static void dp_audio_safe_to_exit_level(struct dp_audio_private *audio)
|
||||
safe_to_exit_level = 5;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DP("setting the default safe_to_exit_level = %u\n",
|
||||
drm_dbg_dp(audio->drm_dev,
|
||||
"setting the default safe_to_exit_level = %u\n",
|
||||
safe_to_exit_level);
|
||||
safe_to_exit_level = 14;
|
||||
break;
|
||||
|
@ -34,6 +34,7 @@ struct dp_aux_private {
|
||||
bool no_send_addr;
|
||||
bool no_send_stop;
|
||||
bool initted;
|
||||
bool is_edp;
|
||||
u32 offset;
|
||||
u32 segment;
|
||||
|
||||
@ -337,6 +338,22 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* For eDP it's important to give a reasonably long wait here for HPD
|
||||
* to be asserted. This is because the panel driver may have _just_
|
||||
* turned on the panel and then tried to do an AUX transfer. The panel
|
||||
* driver has no way of knowing when the panel is ready, so it's up
|
||||
* to us to wait. For DP we never get into this situation so let's
|
||||
* avoid ever doing the extra long wait for DP.
|
||||
*/
|
||||
if (aux->is_edp) {
|
||||
ret = dp_catalog_aux_wait_for_hpd_connect_state(aux->catalog);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DP("Panel not ready for aux transactions\n");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
dp_aux_update_offset_and_segment(aux, msg);
|
||||
dp_aux_transfer_helper(aux, msg, true);
|
||||
|
||||
@ -491,7 +508,8 @@ void dp_aux_unregister(struct drm_dp_aux *dp_aux)
|
||||
drm_dp_aux_unregister(dp_aux);
|
||||
}
|
||||
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog)
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,
|
||||
bool is_edp)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
@ -506,6 +524,7 @@ struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog)
|
||||
|
||||
init_completion(&aux->comp);
|
||||
aux->cmd_busy = false;
|
||||
aux->is_edp = is_edp;
|
||||
mutex_init(&aux->mutex);
|
||||
|
||||
aux->dev = dev;
|
||||
|
@ -16,7 +16,8 @@ void dp_aux_init(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_deinit(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_reconfig(struct drm_dp_aux *dp_aux);
|
||||
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog);
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog,
|
||||
bool is_edp);
|
||||
void dp_aux_put(struct drm_dp_aux *aux);
|
||||
|
||||
#endif /*__DP_AUX_H_*/
|
||||
|
@ -24,6 +24,8 @@
|
||||
#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
|
||||
#define DP_INTERRUPT_STATUS_MASK_SHIFT 2
|
||||
|
||||
#define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4)
|
||||
|
||||
#define DP_INTERRUPT_STATUS1 \
|
||||
(DP_INTR_AUX_I2C_DONE| \
|
||||
DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
|
||||
@ -47,6 +49,7 @@
|
||||
|
||||
struct dp_catalog_private {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct dp_io *io;
|
||||
u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
|
||||
struct dp_catalog dp_catalog;
|
||||
@ -80,7 +83,7 @@ static inline void dp_write_aux(struct dp_catalog_private *catalog,
|
||||
writel(data, catalog->io->dp_controller.aux.base + offset);
|
||||
}
|
||||
|
||||
static inline u32 dp_read_ahb(struct dp_catalog_private *catalog, u32 offset)
|
||||
static inline u32 dp_read_ahb(const struct dp_catalog_private *catalog, u32 offset)
|
||||
{
|
||||
return readl_relaxed(catalog->io->dp_controller.ahb.base + offset);
|
||||
}
|
||||
@ -242,6 +245,19 @@ void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog)
|
||||
phy_calibrate(phy);
|
||||
}
|
||||
|
||||
int dp_catalog_aux_wait_for_hpd_connect_state(struct dp_catalog *dp_catalog)
|
||||
{
|
||||
u32 state;
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
/* poll for hpd connected status every 2ms and timeout after 500ms */
|
||||
return readl_poll_timeout(catalog->io->dp_controller.aux.base +
|
||||
REG_DP_DP_HPD_INT_STATUS,
|
||||
state, state & DP_DP_HPD_STATE_STATUS_CONNECTED,
|
||||
2000, 500000);
|
||||
}
|
||||
|
||||
static void dump_regs(void __iomem *base, int len)
|
||||
{
|
||||
int i;
|
||||
@ -322,7 +338,7 @@ void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 cfg)
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
DRM_DEBUG_DP("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
|
||||
drm_dbg_dp(catalog->drm_dev, "DP_CONFIGURATION_CTRL=0x%x\n", cfg);
|
||||
|
||||
dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
|
||||
}
|
||||
@ -350,7 +366,7 @@ void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
DRM_DEBUG_DP("enable=%d\n", enable);
|
||||
drm_dbg_dp(catalog->drm_dev, "enable=%d\n", enable);
|
||||
if (enable) {
|
||||
/*
|
||||
* To make sure link reg writes happens before other operation,
|
||||
@ -395,7 +411,7 @@ void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog,
|
||||
/* Configure clock to synchronous mode */
|
||||
misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
|
||||
|
||||
DRM_DEBUG_DP("misc settings = 0x%x\n", misc_val);
|
||||
drm_dbg_dp(catalog->drm_dev, "misc settings = 0x%x\n", misc_val);
|
||||
dp_write_link(catalog, REG_DP_MISC1_MISC0, misc_val);
|
||||
}
|
||||
|
||||
@ -450,7 +466,7 @@ void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
|
||||
if (link_rate_hbr3 == rate)
|
||||
nvid *= 3;
|
||||
|
||||
DRM_DEBUG_DP("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
|
||||
drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
|
||||
dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
|
||||
dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
|
||||
dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
|
||||
@ -465,7 +481,7 @@ int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
bit = BIT(state_bit - 1);
|
||||
DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, state_bit);
|
||||
drm_dbg_dp(catalog->drm_dev, "hw: bit=%d train=%d\n", bit, state_bit);
|
||||
dp_catalog_ctrl_state_ctrl(dp_catalog, bit);
|
||||
|
||||
bit = BIT(state_bit - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
|
||||
@ -482,6 +498,22 @@ int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_catalog_hw_revision() - retrieve DP hw revision
|
||||
*
|
||||
* @dp_catalog: DP catalog structure
|
||||
*
|
||||
* Return: DP controller hw revision
|
||||
*
|
||||
*/
|
||||
u32 dp_catalog_hw_revision(const struct dp_catalog *dp_catalog)
|
||||
{
|
||||
const struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
return dp_read_ahb(catalog, REG_DP_HW_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_catalog_ctrl_reset() - reset DP controller
|
||||
*
|
||||
@ -557,7 +589,8 @@ void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
|
||||
|
||||
config = (en ? config | intr_mask : config & ~intr_mask);
|
||||
|
||||
DRM_DEBUG_DP("intr_mask=%#x config=%#x\n", intr_mask, config);
|
||||
drm_dbg_dp(catalog->drm_dev, "intr_mask=%#x config=%#x\n",
|
||||
intr_mask, config);
|
||||
dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
|
||||
config & DP_DP_HPD_INT_MASK);
|
||||
}
|
||||
@ -569,10 +602,6 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
|
||||
|
||||
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
|
||||
|
||||
/* enable HPD plug and unplug interrupts */
|
||||
dp_catalog_hpd_config_intr(dp_catalog,
|
||||
DP_DP_HPD_PLUG_INT_MASK | DP_DP_HPD_UNPLUG_INT_MASK, true);
|
||||
|
||||
/* Configure REFTIMER and enable it */
|
||||
reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
|
||||
dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);
|
||||
@ -588,7 +617,7 @@ u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog)
|
||||
u32 status;
|
||||
|
||||
status = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
|
||||
DRM_DEBUG_DP("aux status: %#x\n", status);
|
||||
drm_dbg_dp(catalog->drm_dev, "aux status: %#x\n", status);
|
||||
status >>= DP_DP_HPD_STATE_STATUS_BITS_SHIFT;
|
||||
status &= DP_DP_HPD_STATE_STATUS_BITS_MASK;
|
||||
|
||||
@ -599,13 +628,21 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
|
||||
{
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
int isr = 0;
|
||||
int isr, mask;
|
||||
|
||||
isr = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
|
||||
dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
|
||||
(isr & DP_DP_HPD_INT_MASK));
|
||||
mask = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);
|
||||
|
||||
return isr;
|
||||
/*
|
||||
* We only want to return interrupts that are unmasked to the caller.
|
||||
* However, the interrupt status field also contains other
|
||||
* informational bits about the HPD state status, so we only mask
|
||||
* out the part of the register that tells us about which interrupts
|
||||
* are pending.
|
||||
*/
|
||||
return isr & (mask | ~DP_DP_HPD_INT_MASK);
|
||||
}
|
||||
|
||||
int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
|
||||
@ -664,7 +701,7 @@ void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
|
||||
/* Make sure to clear the current pattern before starting a new one */
|
||||
dp_write_link(catalog, REG_DP_STATE_CTRL, 0x0);
|
||||
|
||||
DRM_DEBUG_DP("pattern: %#x\n", pattern);
|
||||
drm_dbg_dp(catalog->drm_dev, "pattern: %#x\n", pattern);
|
||||
switch (pattern) {
|
||||
case DP_PHY_TEST_PATTERN_D10_2:
|
||||
dp_write_link(catalog, REG_DP_STATE_CTRL,
|
||||
@ -725,7 +762,8 @@ void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
|
||||
DP_STATE_CTRL_LINK_TRAINING_PATTERN4);
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DP("No valid test pattern requested: %#x\n", pattern);
|
||||
drm_dbg_dp(catalog->drm_dev,
|
||||
"No valid test pattern requested: %#x\n", pattern);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -743,6 +781,7 @@ int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog)
|
||||
{
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
u32 reg;
|
||||
|
||||
dp_write_link(catalog, REG_DP_TOTAL_HOR_VER,
|
||||
dp_catalog->total);
|
||||
@ -751,7 +790,18 @@ int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog)
|
||||
dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY,
|
||||
dp_catalog->width_blanking);
|
||||
dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, dp_catalog->dp_active);
|
||||
dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0);
|
||||
|
||||
reg = dp_read_p0(catalog, MMSS_DP_INTF_CONFIG);
|
||||
|
||||
if (dp_catalog->wide_bus_en)
|
||||
reg |= DP_INTF_CONFIG_DATABUS_WIDEN;
|
||||
else
|
||||
reg &= ~DP_INTF_CONFIG_DATABUS_WIDEN;
|
||||
|
||||
|
||||
DRM_DEBUG_DP("wide_bus_en=%d reg=%#x\n", dp_catalog->wide_bus_en, reg);
|
||||
|
||||
dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -820,7 +870,7 @@ void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
|
||||
DP_BIST_ENABLE_DPBIST_EN);
|
||||
dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN,
|
||||
DP_TIMING_ENGINE_EN_EN);
|
||||
DRM_DEBUG_DP("%s: enabled tpg\n", __func__);
|
||||
drm_dbg_dp(catalog->drm_dev, "%s: enabled tpg\n", __func__);
|
||||
}
|
||||
|
||||
void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog)
|
||||
@ -909,7 +959,8 @@ void dp_catalog_audio_config_acr(struct dp_catalog *dp_catalog)
|
||||
select = dp_catalog->audio_data;
|
||||
acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
|
||||
|
||||
DRM_DEBUG_DP("select: %#x, acr_ctrl: %#x\n", select, acr_ctrl);
|
||||
drm_dbg_dp(catalog->drm_dev, "select: %#x, acr_ctrl: %#x\n",
|
||||
select, acr_ctrl);
|
||||
|
||||
dp_write_link(catalog, MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
|
||||
}
|
||||
@ -934,7 +985,7 @@ void dp_catalog_audio_enable(struct dp_catalog *dp_catalog)
|
||||
else
|
||||
audio_ctrl &= ~BIT(0);
|
||||
|
||||
DRM_DEBUG_DP("dp_audio_cfg = 0x%x\n", audio_ctrl);
|
||||
drm_dbg_dp(catalog->drm_dev, "dp_audio_cfg = 0x%x\n", audio_ctrl);
|
||||
|
||||
dp_write_link(catalog, MMSS_DP_AUDIO_CFG, audio_ctrl);
|
||||
/* make sure audio engine is disabled */
|
||||
@ -965,7 +1016,7 @@ void dp_catalog_audio_config_sdp(struct dp_catalog *dp_catalog)
|
||||
/* AUDIO_INFOFRAME_SDP_EN */
|
||||
sdp_cfg |= BIT(20);
|
||||
|
||||
DRM_DEBUG_DP("sdp_cfg = 0x%x\n", sdp_cfg);
|
||||
drm_dbg_dp(catalog->drm_dev, "sdp_cfg = 0x%x\n", sdp_cfg);
|
||||
|
||||
dp_write_link(catalog, MMSS_DP_SDP_CFG, sdp_cfg);
|
||||
|
||||
@ -975,7 +1026,7 @@ void dp_catalog_audio_config_sdp(struct dp_catalog *dp_catalog)
|
||||
/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
|
||||
sdp_cfg2 &= ~BIT(1);
|
||||
|
||||
DRM_DEBUG_DP("sdp_cfg2 = 0x%x\n", sdp_cfg2);
|
||||
drm_dbg_dp(catalog->drm_dev, "sdp_cfg2 = 0x%x\n", sdp_cfg2);
|
||||
|
||||
dp_write_link(catalog, MMSS_DP_SDP_CFG2, sdp_cfg2);
|
||||
}
|
||||
@ -1037,7 +1088,8 @@ void dp_catalog_audio_sfe_level(struct dp_catalog *dp_catalog)
|
||||
mainlink_levels &= 0xFE0;
|
||||
mainlink_levels |= safe_to_exit_level;
|
||||
|
||||
DRM_DEBUG_DP("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
|
||||
drm_dbg_dp(catalog->drm_dev,
|
||||
"mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
|
||||
mainlink_levels, safe_to_exit_level);
|
||||
|
||||
dp_write_link(catalog, REG_DP_MAINLINK_LEVELS, mainlink_levels);
|
||||
|
@ -70,6 +70,7 @@ struct dp_catalog {
|
||||
enum dp_catalog_audio_sdp_type sdp_type;
|
||||
enum dp_catalog_audio_header_type sdp_header;
|
||||
u32 audio_data;
|
||||
bool wide_bus_en;
|
||||
};
|
||||
|
||||
/* Debug module */
|
||||
@ -84,6 +85,7 @@ int dp_catalog_aux_clear_hw_interrupts(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_aux_reset(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable);
|
||||
void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog);
|
||||
int dp_catalog_aux_wait_for_hpd_connect_state(struct dp_catalog *dp_catalog);
|
||||
u32 dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog);
|
||||
|
||||
/* DP Controller APIs */
|
||||
@ -95,6 +97,7 @@ void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb);
|
||||
void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate,
|
||||
u32 stream_rate_khz, bool fixed_nvid);
|
||||
int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, u32 pattern);
|
||||
u32 dp_catalog_hw_revision(const struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
|
||||
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
|
||||
|
@ -70,6 +70,7 @@ struct dp_vc_tu_mapping_table {
|
||||
|
||||
struct dp_ctrl_private {
|
||||
struct dp_ctrl dp_ctrl;
|
||||
struct drm_device *drm_dev;
|
||||
struct device *dev;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_panel *panel;
|
||||
@ -114,7 +115,7 @@ void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
|
||||
IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
|
||||
pr_warn("PUSH_IDLE pattern timedout\n");
|
||||
|
||||
DRM_DEBUG_DP("mainlink off done\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "mainlink off\n");
|
||||
}
|
||||
|
||||
static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
|
||||
@ -603,8 +604,9 @@ static void _tu_valid_boundary_calc(struct tu_algo_data *tu)
|
||||
}
|
||||
}
|
||||
|
||||
static void _dp_ctrl_calc_tu(struct dp_tu_calc_input *in,
|
||||
struct dp_vc_tu_mapping_table *tu_table)
|
||||
static void _dp_ctrl_calc_tu(struct dp_ctrl_private *ctrl,
|
||||
struct dp_tu_calc_input *in,
|
||||
struct dp_vc_tu_mapping_table *tu_table)
|
||||
{
|
||||
struct tu_algo_data *tu;
|
||||
int compare_result_1, compare_result_2;
|
||||
@ -687,8 +689,8 @@ static void _dp_ctrl_calc_tu(struct dp_tu_calc_input *in,
|
||||
|
||||
if (tu->dsc_en && compare_result_1 && compare_result_2) {
|
||||
HBLANK_MARGIN += 4;
|
||||
DRM_DEBUG_DP("Info: increase HBLANK_MARGIN to %d\n",
|
||||
HBLANK_MARGIN);
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"increase HBLANK_MARGIN to %d\n", HBLANK_MARGIN);
|
||||
}
|
||||
|
||||
tu_size_calc:
|
||||
@ -722,8 +724,10 @@ tu_size_calc:
|
||||
tu->n_tus += 1;
|
||||
|
||||
tu->even_distribution_legacy = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
|
||||
DRM_DEBUG_DP("Info: n_sym = %d, num_of_tus = %d\n",
|
||||
tu->valid_boundary_link, tu->n_tus);
|
||||
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"n_sym = %d, num_of_tus = %d\n",
|
||||
tu->valid_boundary_link, tu->n_tus);
|
||||
|
||||
temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
|
||||
temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
|
||||
@ -916,19 +920,20 @@ tu_size_calc:
|
||||
tu_table->lower_boundary_count = tu->lower_boundary_count;
|
||||
tu_table->tu_size_minus1 = tu->tu_size_minus1;
|
||||
|
||||
DRM_DEBUG_DP("TU: valid_boundary_link: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: valid_boundary_link: %d\n",
|
||||
tu_table->valid_boundary_link);
|
||||
DRM_DEBUG_DP("TU: delay_start_link: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: delay_start_link: %d\n",
|
||||
tu_table->delay_start_link);
|
||||
DRM_DEBUG_DP("TU: boundary_moderation_en: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: boundary_moderation_en: %d\n",
|
||||
tu_table->boundary_moderation_en);
|
||||
DRM_DEBUG_DP("TU: valid_lower_boundary_link: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: valid_lower_boundary_link: %d\n",
|
||||
tu_table->valid_lower_boundary_link);
|
||||
DRM_DEBUG_DP("TU: upper_boundary_count: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: upper_boundary_count: %d\n",
|
||||
tu_table->upper_boundary_count);
|
||||
DRM_DEBUG_DP("TU: lower_boundary_count: %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: lower_boundary_count: %d\n",
|
||||
tu_table->lower_boundary_count);
|
||||
DRM_DEBUG_DP("TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1);
|
||||
drm_dbg_dp(ctrl->drm_dev, "TU: tu_size_minus1: %d\n",
|
||||
tu_table->tu_size_minus1);
|
||||
|
||||
kfree(tu);
|
||||
}
|
||||
@ -954,7 +959,7 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
|
||||
in.num_of_dsc_slices = 0;
|
||||
in.compress_ratio = 100;
|
||||
|
||||
_dp_ctrl_calc_tu(&in, tu_table);
|
||||
_dp_ctrl_calc_tu(ctrl, &in, tu_table);
|
||||
}
|
||||
|
||||
static void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
|
||||
@ -1005,8 +1010,9 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
|
||||
u32 voltage_swing_level = link->phy_params.v_level;
|
||||
u32 pre_emphasis_level = link->phy_params.p_level;
|
||||
|
||||
DRM_DEBUG_DP("voltage level: %d emphasis level: %d\n", voltage_swing_level,
|
||||
pre_emphasis_level);
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"voltage level: %d emphasis level: %d\n",
|
||||
voltage_swing_level, pre_emphasis_level);
|
||||
ret = dp_catalog_ctrl_update_vx_px(ctrl->catalog,
|
||||
voltage_swing_level, pre_emphasis_level);
|
||||
|
||||
@ -1014,13 +1020,15 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
|
||||
return ret;
|
||||
|
||||
if (voltage_swing_level >= DP_TRAIN_VOLTAGE_SWING_MAX) {
|
||||
DRM_DEBUG_DP("max. voltage swing level reached %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"max. voltage swing level reached %d\n",
|
||||
voltage_swing_level);
|
||||
max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;
|
||||
}
|
||||
|
||||
if (pre_emphasis_level >= DP_TRAIN_PRE_EMPHASIS_MAX) {
|
||||
DRM_DEBUG_DP("max. pre-emphasis level reached %d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"max. pre-emphasis level reached %d\n",
|
||||
pre_emphasis_level);
|
||||
max_level_reached |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
|
||||
}
|
||||
@ -1032,8 +1040,8 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
|
||||
buf[lane] = voltage_swing_level | pre_emphasis_level
|
||||
| max_level_reached;
|
||||
|
||||
DRM_DEBUG_DP("sink: p|v=0x%x\n", voltage_swing_level
|
||||
| pre_emphasis_level);
|
||||
drm_dbg_dp(ctrl->drm_dev, "sink: p|v=0x%x\n",
|
||||
voltage_swing_level | pre_emphasis_level);
|
||||
ret = drm_dp_dpcd_write(ctrl->aux, DP_TRAINING_LANE0_SET,
|
||||
buf, lane_cnt);
|
||||
if (ret == lane_cnt)
|
||||
@ -1048,7 +1056,7 @@ static bool dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
|
||||
u8 buf;
|
||||
int ret = 0;
|
||||
|
||||
DRM_DEBUG_DP("sink: pattern=%x\n", pattern);
|
||||
drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern);
|
||||
|
||||
buf = pattern;
|
||||
|
||||
@ -1119,8 +1127,6 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl,
|
||||
old_v_level = ctrl->link->phy_params.v_level;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("clock recovery not done, adjusting vx px\n");
|
||||
|
||||
dp_link_adjust_levels(ctrl->link, link_status);
|
||||
ret = dp_ctrl_update_vx_px(ctrl);
|
||||
if (ret)
|
||||
@ -1151,8 +1157,10 @@ static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
DRM_DEBUG_DP("new rate=0x%x\n", ctrl->link->link_params.rate);
|
||||
if (!ret) {
|
||||
drm_dbg_dp(ctrl->drm_dev, "new rate=0x%x\n",
|
||||
ctrl->link->link_params.rate);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1271,7 +1279,7 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
|
||||
}
|
||||
|
||||
/* print success info as this is a result of user initiated action */
|
||||
DRM_DEBUG_DP("link training #1 successful\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "link training #1 successful\n");
|
||||
|
||||
ret = dp_ctrl_link_train_2(ctrl, training_step);
|
||||
if (ret) {
|
||||
@ -1280,7 +1288,7 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
|
||||
}
|
||||
|
||||
/* print success info as this is a result of user initiated action */
|
||||
DRM_DEBUG_DP("link training #2 successful\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "link training #2 successful\n");
|
||||
|
||||
end:
|
||||
dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
|
||||
@ -1320,7 +1328,8 @@ static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
|
||||
cfg++;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("setting rate=%lu on clk=%s\n", rate, name);
|
||||
drm_dbg_dp(ctrl->drm_dev, "setting rate=%lu on clk=%s\n",
|
||||
rate, name);
|
||||
|
||||
if (num)
|
||||
cfg->rate = rate;
|
||||
@ -1350,7 +1359,7 @@ static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
|
||||
if (ret)
|
||||
DRM_ERROR("Unable to start link clocks. ret=%d\n", ret);
|
||||
|
||||
DRM_DEBUG_DP("link rate=%d pixel_clk=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "link rate=%d pixel_clk=%d\n",
|
||||
ctrl->link->link_params.rate, ctrl->dp_ctrl.pixel_rate);
|
||||
|
||||
return ret;
|
||||
@ -1367,7 +1376,7 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
|
||||
if (ret)
|
||||
DRM_ERROR("Unabled to start pixel clocks. ret=%d\n", ret);
|
||||
|
||||
DRM_DEBUG_DP("link rate=%d pixel_clk=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "link rate=%d pixel_clk=%d\n",
|
||||
ctrl->link->link_params.rate, ctrl->dp_ctrl.pixel_rate);
|
||||
|
||||
return ret;
|
||||
@ -1397,7 +1406,8 @@ void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl)
|
||||
|
||||
dp_catalog_ctrl_phy_reset(ctrl->catalog);
|
||||
phy_init(phy);
|
||||
DRM_DEBUG_DP("phy=%p init=%d power_on=%d\n",
|
||||
|
||||
drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
}
|
||||
|
||||
@ -1413,7 +1423,7 @@ void dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl)
|
||||
|
||||
dp_catalog_ctrl_phy_reset(ctrl->catalog);
|
||||
phy_exit(phy);
|
||||
DRM_DEBUG_DP("phy=%p init=%d power_on=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
}
|
||||
|
||||
@ -1489,7 +1499,7 @@ static int dp_ctrl_deinitialize_mainlink(struct dp_ctrl_private *ctrl)
|
||||
phy_exit(phy);
|
||||
phy_init(phy);
|
||||
|
||||
DRM_DEBUG_DP("phy=%p init=%d power_on=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
return 0;
|
||||
}
|
||||
@ -1524,7 +1534,8 @@ static int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
|
||||
int ret = 0;
|
||||
|
||||
if (!ctrl->link->phy_params.phy_test_pattern_sel) {
|
||||
DRM_DEBUG_DP("no test pattern selected by sink\n");
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"no test pattern selected by sink\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1533,7 +1544,7 @@ static int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
|
||||
* running. Add the global reset just before disabling the
|
||||
* link clocks and core clocks.
|
||||
*/
|
||||
ret = dp_ctrl_off_link_stream(&ctrl->dp_ctrl);
|
||||
ret = dp_ctrl_off(&ctrl->dp_ctrl);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to disable DP controller\n");
|
||||
return ret;
|
||||
@ -1554,7 +1565,7 @@ static bool dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
|
||||
u32 pattern_sent = 0x0;
|
||||
u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
|
||||
|
||||
DRM_DEBUG_DP("request: 0x%x\n", pattern_requested);
|
||||
drm_dbg_dp(ctrl->drm_dev, "request: 0x%x\n", pattern_requested);
|
||||
|
||||
if (dp_catalog_ctrl_update_vx_px(ctrl->catalog,
|
||||
ctrl->link->phy_params.v_level,
|
||||
@ -1595,8 +1606,8 @@ static bool dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
|
||||
success = false;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("%s: test->0x%x\n", success ? "success" : "failed",
|
||||
pattern_requested);
|
||||
drm_dbg_dp(ctrl->drm_dev, "%s: test->0x%x\n",
|
||||
success ? "success" : "failed", pattern_requested);
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -1614,7 +1625,7 @@ void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
|
||||
sink_request = ctrl->link->sink_request;
|
||||
|
||||
if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
|
||||
DRM_DEBUG_DP("PHY_TEST_PATTERN request\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
|
||||
if (dp_ctrl_process_phy_test_request(ctrl)) {
|
||||
DRM_ERROR("process phy_test_req failed\n");
|
||||
return;
|
||||
@ -1686,7 +1697,8 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
|
||||
dp_power_clk_enable(ctrl->power, DP_CORE_PM, true);
|
||||
|
||||
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
|
||||
DRM_DEBUG_DP("using phy test link parameters\n");
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"using phy test link parameters\n");
|
||||
if (!ctrl->panel->dp_mode.drm_mode.clock)
|
||||
ctrl->dp_ctrl.pixel_rate = phy_cts_pixel_clk_khz;
|
||||
} else {
|
||||
@ -1696,12 +1708,10 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
|
||||
ctrl->dp_ctrl.pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("rate=%d, num_lanes=%d, pixel_rate=%d\n",
|
||||
ctrl->link->link_params.rate,
|
||||
ctrl->link->link_params.num_lanes, ctrl->dp_ctrl.pixel_rate);
|
||||
drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%d\n",
|
||||
ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes,
|
||||
ctrl->dp_ctrl.pixel_rate);
|
||||
|
||||
ctrl->link->phy_params.p_level = 0;
|
||||
ctrl->link->phy_params.v_level = 0;
|
||||
|
||||
rc = dp_ctrl_enable_mainlink_clocks(ctrl);
|
||||
if (rc)
|
||||
@ -1803,6 +1813,7 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
int ret = 0;
|
||||
bool mainlink_ready = false;
|
||||
struct dp_ctrl_private *ctrl;
|
||||
unsigned long pixel_rate_orig;
|
||||
|
||||
if (!dp_ctrl)
|
||||
return -EINVAL;
|
||||
@ -1811,7 +1822,11 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
|
||||
ctrl->dp_ctrl.pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
|
||||
|
||||
DRM_DEBUG_DP("rate=%d, num_lanes=%d, pixel_rate=%d\n",
|
||||
pixel_rate_orig = ctrl->dp_ctrl.pixel_rate;
|
||||
if (dp_ctrl->wide_bus_en)
|
||||
ctrl->dp_ctrl.pixel_rate >>= 1;
|
||||
|
||||
drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%d\n",
|
||||
ctrl->link->link_params.rate,
|
||||
ctrl->link->link_params.num_lanes, ctrl->dp_ctrl.pixel_rate);
|
||||
|
||||
@ -1823,12 +1838,6 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
}
|
||||
}
|
||||
|
||||
if (!dp_ctrl_channel_eq_ok(ctrl))
|
||||
dp_ctrl_link_retrain(ctrl);
|
||||
|
||||
/* stop txing train pattern to end link training */
|
||||
dp_ctrl_clear_training_pattern(ctrl);
|
||||
|
||||
ret = dp_ctrl_enable_stream_clocks(ctrl);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
|
||||
@ -1840,6 +1849,12 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dp_ctrl_channel_eq_ok(ctrl))
|
||||
dp_ctrl_link_retrain(ctrl);
|
||||
|
||||
/* stop txing train pattern to end link training */
|
||||
dp_ctrl_clear_training_pattern(ctrl);
|
||||
|
||||
/*
|
||||
* Set up transfer unit values and set controller state to send
|
||||
* video.
|
||||
@ -1850,7 +1865,7 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
|
||||
dp_catalog_ctrl_config_msa(ctrl->catalog,
|
||||
ctrl->link->link_params.rate,
|
||||
ctrl->dp_ctrl.pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
|
||||
pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl));
|
||||
|
||||
dp_ctrl_setup_tr_unit(ctrl);
|
||||
|
||||
@ -1861,7 +1876,8 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
|
||||
return ret;
|
||||
|
||||
mainlink_ready = dp_catalog_ctrl_mainlink_ready(ctrl->catalog);
|
||||
DRM_DEBUG_DP("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
|
||||
drm_dbg_dp(ctrl->drm_dev,
|
||||
"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
|
||||
|
||||
end:
|
||||
return ret;
|
||||
@ -1897,20 +1913,46 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("Before, phy=%x init_count=%d power_on=%d\n",
|
||||
(u32)(uintptr_t)phy, phy->init_count, phy->power_count);
|
||||
|
||||
phy_power_off(phy);
|
||||
|
||||
/* aux channel down, reinit phy */
|
||||
phy_exit(phy);
|
||||
phy_init(phy);
|
||||
|
||||
DRM_DEBUG_DP("phy=%p init=%d power_on=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dp_ctrl_off_link(struct dp_ctrl *dp_ctrl)
|
||||
{
|
||||
struct dp_ctrl_private *ctrl;
|
||||
struct dp_io *dp_io;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
|
||||
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
|
||||
dp_io = &ctrl->parser->io;
|
||||
phy = dp_io->phy;
|
||||
|
||||
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
|
||||
|
||||
ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("Before, phy=%p init_count=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
|
||||
phy_power_off(phy);
|
||||
|
||||
DRM_DEBUG_DP("After, phy=%p init_count=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
|
||||
{
|
||||
struct dp_ctrl_private *ctrl;
|
||||
@ -1939,7 +1981,7 @@ int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
|
||||
}
|
||||
|
||||
phy_power_off(phy);
|
||||
DRM_DEBUG_DP("phy=%p init=%d power_on=%d\n",
|
||||
drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
|
||||
phy, phy->init_count, phy->power_count);
|
||||
|
||||
return ret;
|
||||
@ -1958,12 +2000,12 @@ void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
|
||||
isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
|
||||
|
||||
if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
|
||||
DRM_DEBUG_DP("dp_video_ready\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "dp_video_ready\n");
|
||||
complete(&ctrl->video_comp);
|
||||
}
|
||||
|
||||
if (isr & DP_CTRL_INTR_IDLE_PATTERN_SENT) {
|
||||
DRM_DEBUG_DP("idle_patterns_sent\n");
|
||||
drm_dbg_dp(ctrl->drm_dev, "idle_patterns_sent\n");
|
||||
complete(&ctrl->idle_comp);
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,13 @@ struct dp_ctrl {
|
||||
bool orientation;
|
||||
atomic_t aborted;
|
||||
u32 pixel_rate;
|
||||
bool wide_bus_en;
|
||||
};
|
||||
|
||||
int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_off_link(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_off(struct dp_ctrl *dp_ctrl);
|
||||
void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl);
|
||||
void dp_ctrl_isr(struct dp_ctrl *dp_ctrl);
|
||||
|
@ -44,8 +44,6 @@ static int dp_debug_show(struct seq_file *seq, void *p)
|
||||
drm_mode = &debug->panel->dp_mode.drm_mode;
|
||||
|
||||
seq_printf(seq, "\tname = %s\n", DEBUG_NAME);
|
||||
seq_printf(seq, "\tdp_panel\n\t\tmax_pclk_khz = %d\n",
|
||||
debug->panel->max_pclk_khz);
|
||||
seq_printf(seq, "\tdrm_dp_link\n\t\trate = %u\n",
|
||||
debug->panel->link_info.rate);
|
||||
seq_printf(seq, "\t\tnum_lanes = %u\n",
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/display/drm_dp_aux_bus.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_kms.h"
|
||||
@ -42,7 +42,7 @@ enum {
|
||||
/* event thread connection state */
|
||||
enum {
|
||||
ST_DISCONNECTED,
|
||||
ST_CONNECT_PENDING,
|
||||
ST_MAINLINK_READY,
|
||||
ST_CONNECTED,
|
||||
ST_DISCONNECT_PENDING,
|
||||
ST_DISPLAY_OFF,
|
||||
@ -57,14 +57,11 @@ enum {
|
||||
EV_IRQ_HPD_INT,
|
||||
EV_HPD_UNPLUG_INT,
|
||||
EV_USER_NOTIFICATION,
|
||||
EV_CONNECT_PENDING_TIMEOUT,
|
||||
EV_DISCONNECT_PENDING_TIMEOUT,
|
||||
};
|
||||
|
||||
#define EVENT_TIMEOUT (HZ/10) /* 100ms */
|
||||
#define DP_EVENT_Q_MAX 8
|
||||
|
||||
#define DP_TIMEOUT_5_SECOND (5000/EVENT_TIMEOUT)
|
||||
#define DP_TIMEOUT_NONE 0
|
||||
|
||||
#define WAIT_FOR_RESUME_TIMEOUT_JIFFIES (HZ / 2)
|
||||
@ -87,6 +84,7 @@ struct dp_display_private {
|
||||
bool hpd_irq_on;
|
||||
bool audio_supported;
|
||||
|
||||
struct drm_device *drm_dev;
|
||||
struct platform_device *pdev;
|
||||
struct dentry *root;
|
||||
|
||||
@ -113,15 +111,19 @@ struct dp_display_private {
|
||||
u32 hpd_state;
|
||||
u32 event_pndx;
|
||||
u32 event_gndx;
|
||||
struct task_struct *ev_tsk;
|
||||
struct dp_event event_list[DP_EVENT_Q_MAX];
|
||||
spinlock_t event_lock;
|
||||
|
||||
bool wide_bus_en;
|
||||
|
||||
struct dp_audio *audio;
|
||||
};
|
||||
|
||||
struct msm_dp_desc {
|
||||
phys_addr_t io_start;
|
||||
unsigned int connector_type;
|
||||
bool wide_bus_en;
|
||||
};
|
||||
|
||||
struct msm_dp_config {
|
||||
@ -138,8 +140,8 @@ static const struct msm_dp_config sc7180_dp_cfg = {
|
||||
|
||||
static const struct msm_dp_config sc7280_dp_cfg = {
|
||||
.descs = (const struct msm_dp_desc[]) {
|
||||
[MSM_DP_CONTROLLER_0] = { .io_start = 0x0ae90000, .connector_type = DRM_MODE_CONNECTOR_DisplayPort },
|
||||
[MSM_DP_CONTROLLER_1] = { .io_start = 0x0aea0000, .connector_type = DRM_MODE_CONNECTOR_eDP },
|
||||
[MSM_DP_CONTROLLER_0] = { .io_start = 0x0ae90000, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true },
|
||||
[MSM_DP_CONTROLLER_1] = { .io_start = 0x0aea0000, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true },
|
||||
},
|
||||
.num_descs = 2,
|
||||
};
|
||||
@ -249,6 +251,8 @@ void dp_display_signal_audio_complete(struct msm_dp *dp_display)
|
||||
complete_all(&dp->audio_comp);
|
||||
}
|
||||
|
||||
static int dp_hpd_event_thread_start(struct dp_display_private *dp_priv);
|
||||
|
||||
static int dp_display_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
@ -260,14 +264,14 @@ static int dp_display_bind(struct device *dev, struct device *master,
|
||||
dp->dp_display.drm_dev = drm;
|
||||
priv->dp[dp->id] = &dp->dp_display;
|
||||
|
||||
rc = dp->parser->parse(dp->parser, dp->dp_display.connector_type);
|
||||
rc = dp->parser->parse(dp->parser);
|
||||
if (rc) {
|
||||
DRM_ERROR("device tree parsing failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->dp_display.next_bridge = dp->parser->next_bridge;
|
||||
|
||||
dp->drm_dev = drm;
|
||||
dp->aux->drm_dev = drm;
|
||||
rc = dp_aux_register(dp->aux);
|
||||
if (rc) {
|
||||
@ -282,9 +286,18 @@ static int dp_display_bind(struct device *dev, struct device *master,
|
||||
}
|
||||
|
||||
rc = dp_register_audio_driver(dev, dp->audio);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
DRM_ERROR("Audio registration Dp failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_hpd_event_thread_start(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("Event thread create failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
return 0;
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
@ -295,6 +308,11 @@ static void dp_display_unbind(struct device *dev, struct device *master,
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
struct msm_drm_private *priv = dev_get_drvdata(master);
|
||||
|
||||
/* disable all HPD interrupts */
|
||||
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_INT_MASK, false);
|
||||
|
||||
kthread_stop(dp->ev_tsk);
|
||||
|
||||
dp_power_client_deinit(dp->power);
|
||||
dp_aux_unregister(dp->aux);
|
||||
priv->dp[dp->id] = NULL;
|
||||
@ -313,7 +331,8 @@ static bool dp_display_is_ds_bridge(struct dp_panel *panel)
|
||||
|
||||
static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
|
||||
{
|
||||
DRM_DEBUG_DP("present=%#x sink_count=%d\n", dp->panel->dpcd[DP_DOWNSTREAMPORT_PRESENT],
|
||||
drm_dbg_dp(dp->drm_dev, "present=%#x sink_count=%d\n",
|
||||
dp->panel->dpcd[DP_DOWNSTREAMPORT_PRESENT],
|
||||
dp->link->sink_count);
|
||||
return dp_display_is_ds_bridge(dp->panel) &&
|
||||
(dp->link->sink_count == 0);
|
||||
@ -336,7 +355,8 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,
|
||||
{
|
||||
if ((hpd && dp->dp_display.is_connected) ||
|
||||
(!hpd && !dp->dp_display.is_connected)) {
|
||||
DRM_DEBUG_DP("HPD already %s\n", (hpd ? "on" : "off"));
|
||||
drm_dbg_dp(dp->drm_dev, "HPD already %s\n",
|
||||
(hpd ? "on" : "off"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -346,7 +366,8 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,
|
||||
|
||||
dp->dp_display.is_connected = hpd;
|
||||
|
||||
DRM_DEBUG_DP("hpd=%d\n", hpd);
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d hpd=%d\n",
|
||||
dp->dp_display.connector_type, hpd);
|
||||
dp_display_send_hpd_event(&dp->dp_display);
|
||||
|
||||
return 0;
|
||||
@ -370,7 +391,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
|
||||
dp->audio_supported = drm_detect_monitor_audio(edid);
|
||||
dp_panel_handle_sink_request(dp->panel);
|
||||
|
||||
dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
|
||||
dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes;
|
||||
|
||||
/*
|
||||
@ -394,7 +414,7 @@ end:
|
||||
|
||||
static void dp_display_host_phy_init(struct dp_display_private *dp)
|
||||
{
|
||||
DRM_DEBUG_DP("type=%d core_init=%d phy_init=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d core_init=%d phy_init=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized);
|
||||
|
||||
@ -406,7 +426,7 @@ static void dp_display_host_phy_init(struct dp_display_private *dp)
|
||||
|
||||
static void dp_display_host_phy_exit(struct dp_display_private *dp)
|
||||
{
|
||||
DRM_DEBUG_DP("type=%d core_init=%d phy_init=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d core_init=%d phy_init=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized);
|
||||
|
||||
@ -418,7 +438,7 @@ static void dp_display_host_phy_exit(struct dp_display_private *dp)
|
||||
|
||||
static void dp_display_host_init(struct dp_display_private *dp)
|
||||
{
|
||||
DRM_DEBUG_DP("type=%d core_init=%d phy_init=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d core_init=%d phy_init=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized);
|
||||
|
||||
@ -430,7 +450,7 @@ static void dp_display_host_init(struct dp_display_private *dp)
|
||||
|
||||
static void dp_display_host_deinit(struct dp_display_private *dp)
|
||||
{
|
||||
DRM_DEBUG_DP("type=%d core_init=%d phy_init=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d core_init=%d phy_init=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized);
|
||||
|
||||
@ -450,6 +470,11 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_disconnect_cb(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_notify_disconnect(struct device *dev)
|
||||
{
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
|
||||
@ -471,14 +496,14 @@ static int dp_display_handle_port_ststus_changed(struct dp_display_private *dp)
|
||||
int rc = 0;
|
||||
|
||||
if (dp_display_is_sink_count_zero(dp)) {
|
||||
DRM_DEBUG_DP("sink count is zero, nothing to do\n");
|
||||
drm_dbg_dp(dp->drm_dev, "sink count is zero, nothing to do\n");
|
||||
if (dp->hpd_state != ST_DISCONNECTED) {
|
||||
dp->hpd_state = ST_DISCONNECT_PENDING;
|
||||
dp_add_event(dp, EV_USER_NOTIFICATION, false, 0);
|
||||
}
|
||||
} else {
|
||||
if (dp->hpd_state == ST_DISCONNECTED) {
|
||||
dp->hpd_state = ST_CONNECT_PENDING;
|
||||
dp->hpd_state = ST_MAINLINK_READY;
|
||||
rc = dp_display_process_hpd_high(dp);
|
||||
if (rc)
|
||||
dp->hpd_state = ST_DISCONNECTED;
|
||||
@ -492,10 +517,11 @@ static int dp_display_handle_irq_hpd(struct dp_display_private *dp)
|
||||
{
|
||||
u32 sink_request = dp->link->sink_request;
|
||||
|
||||
DRM_DEBUG_DP("%d\n", sink_request);
|
||||
drm_dbg_dp(dp->drm_dev, "%d\n", sink_request);
|
||||
if (dp->hpd_state == ST_DISCONNECTED) {
|
||||
if (sink_request & DP_LINK_STATUS_UPDATED) {
|
||||
DRM_DEBUG_DP("Disconnected sink_request: %d\n", sink_request);
|
||||
drm_dbg_dp(dp->drm_dev, "Disconnected sink_request: %d\n",
|
||||
sink_request);
|
||||
DRM_ERROR("Disconnected, no DP_LINK_STATUS_UPDATED\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -519,7 +545,8 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
|
||||
rc = dp_link_process_request(dp->link);
|
||||
if (!rc) {
|
||||
sink_request = dp->link->sink_request;
|
||||
DRM_DEBUG_DP("hpd_state=%d sink_request=%d\n", dp->hpd_state, sink_request);
|
||||
drm_dbg_dp(dp->drm_dev, "hpd_state=%d sink_request=%d\n",
|
||||
dp->hpd_state, sink_request);
|
||||
if (sink_request & DS_PORT_STATUS_CHANGED)
|
||||
rc = dp_display_handle_port_ststus_changed(dp);
|
||||
else
|
||||
@ -533,7 +560,6 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
struct dp_usbpd *hpd = dp->usbpd;
|
||||
u32 state;
|
||||
u32 tout = DP_TIMEOUT_5_SECOND;
|
||||
int ret;
|
||||
|
||||
if (!hpd)
|
||||
@ -542,7 +568,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
|
||||
mutex_lock(&dp->event_mutex);
|
||||
|
||||
state = dp->hpd_state;
|
||||
DRM_DEBUG_DP("Before, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
|
||||
if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {
|
||||
@ -550,7 +576,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ST_CONNECT_PENDING || state == ST_CONNECTED) {
|
||||
if (state == ST_MAINLINK_READY || state == ST_CONNECTED) {
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
}
|
||||
@ -562,21 +588,18 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dp->hpd_state = ST_CONNECT_PENDING;
|
||||
|
||||
ret = dp_display_usbpd_configure_cb(&dp->pdev->dev);
|
||||
if (ret) { /* link train failed */
|
||||
dp->hpd_state = ST_DISCONNECTED;
|
||||
} else {
|
||||
/* start sentinel checking in case of missing uevent */
|
||||
dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
|
||||
dp->hpd_state = ST_MAINLINK_READY;
|
||||
}
|
||||
|
||||
/* enable HDP irq_hpd/replug interrupt */
|
||||
dp_catalog_hpd_config_intr(dp->catalog,
|
||||
DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, true);
|
||||
|
||||
DRM_DEBUG_DP("After, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
|
||||
@ -593,23 +616,6 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
|
||||
static int dp_display_enable(struct dp_display_private *dp, u32 data);
|
||||
static int dp_display_disable(struct dp_display_private *dp, u32 data);
|
||||
|
||||
static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
mutex_lock(&dp->event_mutex);
|
||||
|
||||
state = dp->hpd_state;
|
||||
if (state == ST_CONNECT_PENDING) {
|
||||
dp->hpd_state = ST_CONNECTED;
|
||||
DRM_DEBUG_DP("type=%d\n", dp->dp_display.connector_type);
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_display_handle_plugged_change(struct msm_dp *dp_display,
|
||||
bool plugged)
|
||||
{
|
||||
@ -636,7 +642,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
|
||||
|
||||
state = dp->hpd_state;
|
||||
|
||||
DRM_DEBUG_DP("Before, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
|
||||
/* disable irq_hpd/replug interrupts */
|
||||
@ -651,24 +657,21 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
|
||||
if (dp->link->sink_count == 0) {
|
||||
dp_display_host_phy_exit(dp);
|
||||
}
|
||||
dp_display_notify_disconnect(&dp->pdev->dev);
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
} else if (state == ST_DISCONNECT_PENDING) {
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
} else if (state == ST_MAINLINK_READY) {
|
||||
dp_ctrl_off_link(dp->ctrl);
|
||||
dp_display_host_phy_exit(dp);
|
||||
dp->hpd_state = ST_DISCONNECTED;
|
||||
dp_display_notify_disconnect(&dp->pdev->dev);
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ST_DISCONNECT_PENDING) {
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ST_CONNECT_PENDING) {
|
||||
/* wait until CONNECTED */
|
||||
dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 1); /* delay = 1 */
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dp->hpd_state = ST_DISCONNECT_PENDING;
|
||||
|
||||
/* disable HPD plug interrupts */
|
||||
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, false);
|
||||
|
||||
@ -676,18 +679,22 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
|
||||
* We don't need separate work for disconnect as
|
||||
* connect/attention interrupts are disabled
|
||||
*/
|
||||
dp_display_usbpd_disconnect_cb(&dp->pdev->dev);
|
||||
dp_display_notify_disconnect(&dp->pdev->dev);
|
||||
|
||||
/* start sentinel checking in case of missing uevent */
|
||||
dp_add_event(dp, EV_DISCONNECT_PENDING_TIMEOUT, 0, DP_TIMEOUT_5_SECOND);
|
||||
if (state == ST_DISPLAY_OFF) {
|
||||
dp->hpd_state = ST_DISCONNECTED;
|
||||
} else {
|
||||
dp->hpd_state = ST_DISCONNECT_PENDING;
|
||||
}
|
||||
|
||||
/* signal the disconnect event early to ensure proper teardown */
|
||||
dp_display_handle_plugged_change(&dp->dp_display, false);
|
||||
|
||||
/* enable HDP plug interrupt to prepare for next plugin */
|
||||
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true);
|
||||
if (!dp->dp_display.is_edp)
|
||||
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true);
|
||||
|
||||
DRM_DEBUG_DP("After, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
|
||||
/* uevent will complete disconnection part */
|
||||
@ -695,23 +702,6 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
mutex_lock(&dp->event_mutex);
|
||||
|
||||
state = dp->hpd_state;
|
||||
if (state == ST_DISCONNECT_PENDING) {
|
||||
dp->hpd_state = ST_DISCONNECTED;
|
||||
DRM_DEBUG_DP("type=%d\n", dp->dp_display.connector_type);
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
u32 state;
|
||||
@ -720,7 +710,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
|
||||
|
||||
/* irq_hpd can happen at either connected or disconnected state */
|
||||
state = dp->hpd_state;
|
||||
DRM_DEBUG_DP("Before, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
|
||||
if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {
|
||||
@ -728,14 +718,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ST_CONNECT_PENDING) {
|
||||
/* wait until ST_CONNECTED */
|
||||
dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ST_CONNECT_PENDING || state == ST_DISCONNECT_PENDING) {
|
||||
if (state == ST_MAINLINK_READY || state == ST_DISCONNECT_PENDING) {
|
||||
/* wait until ST_CONNECTED */
|
||||
dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
@ -744,7 +727,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
|
||||
|
||||
dp_display_usbpd_attention_cb(&dp->pdev->dev);
|
||||
|
||||
DRM_DEBUG_DP("After, type=%d hpd_state=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n",
|
||||
dp->dp_display.connector_type, state);
|
||||
|
||||
mutex_unlock(&dp->event_mutex);
|
||||
@ -806,7 +789,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->aux = dp_aux_get(dev, dp->catalog);
|
||||
dp->aux = dp_aux_get(dev, dp->catalog, dp->dp_display.is_edp);
|
||||
if (IS_ERR(dp->aux)) {
|
||||
rc = PTR_ERR(dp->aux);
|
||||
DRM_ERROR("failed to initialize aux, rc = %d\n", rc);
|
||||
@ -851,6 +834,10 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
||||
goto error_ctrl;
|
||||
}
|
||||
|
||||
/* populate wide_bus_en to differernt layers */
|
||||
dp->ctrl->wide_bus_en = dp->wide_bus_en;
|
||||
dp->catalog->wide_bus_en = dp->wide_bus_en;
|
||||
|
||||
return rc;
|
||||
|
||||
error_ctrl:
|
||||
@ -885,9 +872,9 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data)
|
||||
int rc = 0;
|
||||
struct msm_dp *dp_display = &dp->dp_display;
|
||||
|
||||
DRM_DEBUG_DP("sink_count=%d\n", dp->link->sink_count);
|
||||
drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
|
||||
if (dp_display->power_on) {
|
||||
DRM_DEBUG_DP("Link already setup, return\n");
|
||||
drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -952,7 +939,7 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
|
||||
|
||||
dp_display->power_on = false;
|
||||
|
||||
DRM_DEBUG_DP("sink count: %d\n", dp->link->sink_count);
|
||||
drm_dbg_dp(dp->drm_dev, "sink count: %d\n", dp->link->sink_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -974,18 +961,42 @@ int dp_display_set_plugged_cb(struct msm_dp *dp_display,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz)
|
||||
/**
|
||||
* dp_bridge_mode_valid - callback to determine if specified mode is valid
|
||||
* @bridge: Pointer to drm bridge structure
|
||||
* @info: display info
|
||||
* @mode: Pointer to drm mode structure
|
||||
* Returns: Validity status for specified mode
|
||||
*/
|
||||
enum drm_mode_status dp_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
const u32 num_components = 3, default_bpp = 24;
|
||||
struct dp_display_private *dp_display;
|
||||
struct dp_link_info *link_info;
|
||||
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
|
||||
struct msm_dp *dp;
|
||||
int mode_pclk_khz = mode->clock;
|
||||
|
||||
dp = to_dp_bridge(bridge)->dp_display;
|
||||
|
||||
if (!dp || !mode_pclk_khz || !dp->connector) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The eDP controller currently does not have a reliable way of
|
||||
* enabling panel power to read sink capabilities. So, we rely
|
||||
* on the panel driver to populate only supported modes for now.
|
||||
*/
|
||||
if (dp->is_edp)
|
||||
return MODE_OK;
|
||||
|
||||
if (mode->clock > DP_MAX_PIXEL_CLK_KHZ)
|
||||
return MODE_BAD;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
link_info = &dp_display->panel->link_info;
|
||||
|
||||
@ -1005,11 +1016,9 @@ int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz)
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
int dp_display_get_modes(struct msm_dp *dp,
|
||||
struct dp_display_mode *dp_mode)
|
||||
int dp_display_get_modes(struct msm_dp *dp)
|
||||
{
|
||||
struct dp_display_private *dp_display;
|
||||
int ret = 0;
|
||||
|
||||
if (!dp) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
@ -1018,11 +1027,8 @@ int dp_display_get_modes(struct msm_dp *dp,
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
ret = dp_panel_get_modes(dp_display->panel,
|
||||
dp->connector, dp_mode);
|
||||
if (dp_mode->drm_mode.clock)
|
||||
dp->max_pclk_khz = dp_mode->drm_mode.clock;
|
||||
return ret;
|
||||
return dp_panel_get_modes(dp_display->panel,
|
||||
dp->connector);
|
||||
}
|
||||
|
||||
bool dp_display_check_video_test(struct msm_dp *dp)
|
||||
@ -1080,6 +1086,13 @@ static void dp_display_config_hpd(struct dp_display_private *dp)
|
||||
dp_display_host_init(dp);
|
||||
dp_catalog_ctrl_hpd_config(dp->catalog);
|
||||
|
||||
/* Enable plug and unplug interrupts only for external DisplayPort */
|
||||
if (!dp->dp_display.is_edp)
|
||||
dp_catalog_hpd_config_intr(dp->catalog,
|
||||
DP_DP_HPD_PLUG_INT_MASK |
|
||||
DP_DP_HPD_UNPLUG_INT_MASK,
|
||||
true);
|
||||
|
||||
/* Enable interrupt first time
|
||||
* we are leaving dp clocks on during disconnect
|
||||
* and never disable interrupt
|
||||
@ -1099,12 +1112,17 @@ static int hpd_event_thread(void *data)
|
||||
while (1) {
|
||||
if (timeout_mode) {
|
||||
wait_event_timeout(dp_priv->event_q,
|
||||
(dp_priv->event_pndx == dp_priv->event_gndx),
|
||||
EVENT_TIMEOUT);
|
||||
(dp_priv->event_pndx == dp_priv->event_gndx) ||
|
||||
kthread_should_stop(), EVENT_TIMEOUT);
|
||||
} else {
|
||||
wait_event_interruptible(dp_priv->event_q,
|
||||
(dp_priv->event_pndx != dp_priv->event_gndx));
|
||||
(dp_priv->event_pndx != dp_priv->event_gndx) ||
|
||||
kthread_should_stop());
|
||||
}
|
||||
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
|
||||
spin_lock_irqsave(&dp_priv->event_lock, flag);
|
||||
todo = &dp_priv->event_list[dp_priv->event_gndx];
|
||||
if (todo->delay) {
|
||||
@ -1158,14 +1176,6 @@ static int hpd_event_thread(void *data)
|
||||
dp_display_send_hpd_notification(dp_priv,
|
||||
todo->data);
|
||||
break;
|
||||
case EV_CONNECT_PENDING_TIMEOUT:
|
||||
dp_connect_pending_timeout(dp_priv,
|
||||
todo->data);
|
||||
break;
|
||||
case EV_DISCONNECT_PENDING_TIMEOUT:
|
||||
dp_disconnect_pending_timeout(dp_priv,
|
||||
todo->data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1174,12 +1184,17 @@ static int hpd_event_thread(void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_hpd_event_setup(struct dp_display_private *dp_priv)
|
||||
static int dp_hpd_event_thread_start(struct dp_display_private *dp_priv)
|
||||
{
|
||||
init_waitqueue_head(&dp_priv->event_q);
|
||||
spin_lock_init(&dp_priv->event_lock);
|
||||
/* set event q to empty */
|
||||
dp_priv->event_gndx = 0;
|
||||
dp_priv->event_pndx = 0;
|
||||
|
||||
kthread_run(hpd_event_thread, dp_priv, "dp_hpd_handler");
|
||||
dp_priv->ev_tsk = kthread_run(hpd_event_thread, dp_priv, "dp_hpd_handler");
|
||||
if (IS_ERR(dp_priv->ev_tsk))
|
||||
return PTR_ERR(dp_priv->ev_tsk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t dp_display_irq_handler(int irq, void *dev_id)
|
||||
@ -1196,15 +1211,13 @@ static irqreturn_t dp_display_irq_handler(int irq, void *dev_id)
|
||||
hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);
|
||||
|
||||
if (hpd_isr_status & 0x0F) {
|
||||
DRM_DEBUG_DP("type=%d isr=0x%x\n",
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d isr=0x%x\n",
|
||||
dp->dp_display.connector_type, hpd_isr_status);
|
||||
/* hpd related interrupts */
|
||||
if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK)
|
||||
dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0);
|
||||
|
||||
if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
|
||||
/* stop sentinel connect pending checking */
|
||||
dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT);
|
||||
dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);
|
||||
}
|
||||
|
||||
@ -1239,10 +1252,9 @@ int dp_display_request_irq(struct msm_dp *dp_display)
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0);
|
||||
if (dp->irq < 0) {
|
||||
rc = dp->irq;
|
||||
DRM_ERROR("failed to get irq: %d\n", rc);
|
||||
return rc;
|
||||
if (!dp->irq) {
|
||||
DRM_ERROR("failed to get irq\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = devm_request_irq(&dp->pdev->dev, dp->irq,
|
||||
@ -1302,6 +1314,9 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
dp->pdev = pdev;
|
||||
dp->name = "drm_dp";
|
||||
dp->dp_display.connector_type = desc->connector_type;
|
||||
dp->wide_bus_en = desc->wide_bus_en;
|
||||
dp->dp_display.is_edp =
|
||||
(dp->dp_display.connector_type == DRM_MODE_CONNECTOR_eDP);
|
||||
|
||||
rc = dp_init_sub_modules(dp);
|
||||
if (rc) {
|
||||
@ -1309,7 +1324,10 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/* setup event q */
|
||||
mutex_init(&dp->event_mutex);
|
||||
init_waitqueue_head(&dp->event_q);
|
||||
spin_lock_init(&dp->event_lock);
|
||||
|
||||
/* Store DP audio handle inside DP display */
|
||||
dp->dp_display.dp_audio = dp->audio;
|
||||
@ -1350,7 +1368,8 @@ static int dp_pm_resume(struct device *dev)
|
||||
|
||||
mutex_lock(&dp->event_mutex);
|
||||
|
||||
DRM_DEBUG_DP("Before, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev,
|
||||
"Before, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized, dp_display->power_on);
|
||||
|
||||
@ -1363,6 +1382,12 @@ static int dp_pm_resume(struct device *dev)
|
||||
dp_catalog_ctrl_hpd_config(dp->catalog);
|
||||
|
||||
|
||||
if (!dp->dp_display.is_edp)
|
||||
dp_catalog_hpd_config_intr(dp->catalog,
|
||||
DP_DP_HPD_PLUG_INT_MASK |
|
||||
DP_DP_HPD_UNPLUG_INT_MASK,
|
||||
true);
|
||||
|
||||
if (dp_catalog_link_is_connected(dp->catalog)) {
|
||||
/*
|
||||
* set sink to normal operation mode -- D0
|
||||
@ -1391,8 +1416,8 @@ static int dp_pm_resume(struct device *dev)
|
||||
dp_display_handle_plugged_change(dp_display, false);
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("After, type=%d sink_count=%d is_connected=%d \
|
||||
core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev,
|
||||
"After, type=%d sink=%d conn=%d core_init=%d phy_init=%d power=%d\n",
|
||||
dp->dp_display.connector_type, dp->link->sink_count,
|
||||
dp->dp_display.is_connected, dp->core_initialized,
|
||||
dp->phy_initialized, dp_display->power_on);
|
||||
@ -1412,7 +1437,8 @@ static int dp_pm_suspend(struct device *dev)
|
||||
|
||||
mutex_lock(&dp->event_mutex);
|
||||
|
||||
DRM_DEBUG_DP("Before, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev,
|
||||
"Before, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized, dp_display->power_on);
|
||||
|
||||
@ -1427,7 +1453,8 @@ static int dp_pm_suspend(struct device *dev)
|
||||
|
||||
dp->hpd_state = ST_SUSPENDED;
|
||||
|
||||
DRM_DEBUG_DP("After, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
drm_dbg_dp(dp->drm_dev,
|
||||
"After, type=%d core_inited=%d phy_inited=%d power_on=%d\n",
|
||||
dp->dp_display.connector_type, dp->core_initialized,
|
||||
dp->phy_initialized, dp_display->power_on);
|
||||
|
||||
@ -1489,9 +1516,17 @@ void msm_dp_irq_postinstall(struct msm_dp *dp_display)
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp_hpd_event_setup(dp);
|
||||
if (!dp_display->is_edp)
|
||||
dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100);
|
||||
}
|
||||
|
||||
dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100);
|
||||
bool msm_dp_wide_bus_available(const struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
return dp->wide_bus_en;
|
||||
}
|
||||
|
||||
void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
|
||||
@ -1513,6 +1548,64 @@ void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
|
||||
}
|
||||
}
|
||||
|
||||
static int dp_display_get_next_bridge(struct msm_dp *dp)
|
||||
{
|
||||
int rc;
|
||||
struct dp_display_private *dp_priv;
|
||||
struct device_node *aux_bus;
|
||||
struct device *dev;
|
||||
|
||||
dp_priv = container_of(dp, struct dp_display_private, dp_display);
|
||||
dev = &dp_priv->pdev->dev;
|
||||
aux_bus = of_get_child_by_name(dev->of_node, "aux-bus");
|
||||
|
||||
if (aux_bus && dp->is_edp) {
|
||||
dp_display_host_init(dp_priv);
|
||||
dp_catalog_ctrl_hpd_config(dp_priv->catalog);
|
||||
dp_display_host_phy_init(dp_priv);
|
||||
enable_irq(dp_priv->irq);
|
||||
|
||||
/*
|
||||
* The code below assumes that the panel will finish probing
|
||||
* by the time devm_of_dp_aux_populate_ep_devices() returns.
|
||||
* This isn't a great assumption since it will fail if the
|
||||
* panel driver is probed asynchronously but is the best we
|
||||
* can do without a bigger driver reorganization.
|
||||
*/
|
||||
rc = devm_of_dp_aux_populate_ep_devices(dp_priv->aux);
|
||||
of_node_put(aux_bus);
|
||||
if (rc)
|
||||
goto error;
|
||||
} else if (dp->is_edp) {
|
||||
DRM_ERROR("eDP aux_bus not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* External bridges are mandatory for eDP interfaces: one has to
|
||||
* provide at least an eDP panel (which gets wrapped into panel-bridge).
|
||||
*
|
||||
* For DisplayPort interfaces external bridges are optional, so
|
||||
* silently ignore an error if one is not present (-ENODEV).
|
||||
*/
|
||||
rc = dp_parser_find_next_bridge(dp_priv->parser);
|
||||
if (!dp->is_edp && rc == -ENODEV)
|
||||
return 0;
|
||||
|
||||
if (!rc) {
|
||||
dp->next_bridge = dp_priv->parser->next_bridge;
|
||||
return 0;
|
||||
}
|
||||
|
||||
error:
|
||||
if (dp->is_edp) {
|
||||
disable_irq(dp_priv->irq);
|
||||
dp_display_host_phy_exit(dp_priv);
|
||||
dp_display_host_deinit(dp_priv);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
@ -1536,6 +1629,21 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
|
||||
dp_display->encoder = encoder;
|
||||
|
||||
ret = dp_display_get_next_bridge(dp_display);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dp_display->bridge = dp_bridge_init(dp_display, dev, encoder);
|
||||
if (IS_ERR(dp_display->bridge)) {
|
||||
ret = PTR_ERR(dp_display->bridge);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"failed to create dp bridge: %d\n", ret);
|
||||
dp_display->bridge = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->bridges[priv->num_bridges++] = dp_display->bridge;
|
||||
|
||||
dp_display->connector = dp_drm_connector_init(dp_display);
|
||||
if (IS_ERR(dp_display->connector)) {
|
||||
ret = PTR_ERR(dp_display->connector);
|
||||
@ -1547,24 +1655,13 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
|
||||
dp_priv->panel->connector = dp_display->connector;
|
||||
|
||||
priv->connectors[priv->num_connectors++] = dp_display->connector;
|
||||
|
||||
dp_display->bridge = msm_dp_bridge_init(dp_display, dev, encoder);
|
||||
if (IS_ERR(dp_display->bridge)) {
|
||||
ret = PTR_ERR(dp_display->bridge);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"failed to create dp bridge: %d\n", ret);
|
||||
dp_display->bridge = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->bridges[priv->num_bridges++] = dp_display->bridge;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
void dp_bridge_enable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
|
||||
struct msm_dp *dp = dp_bridge->dp_display;
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp_display;
|
||||
u32 state;
|
||||
@ -1572,26 +1669,32 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
if (!dp_display->dp_mode.drm_mode.clock) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp->is_edp)
|
||||
dp_hpd_plug_handle(dp_display, 0);
|
||||
|
||||
mutex_lock(&dp_display->event_mutex);
|
||||
|
||||
/* stop sentinel checking */
|
||||
dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT);
|
||||
state = dp_display->hpd_state;
|
||||
if (state != ST_DISPLAY_OFF && state != ST_MAINLINK_READY) {
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = dp_display_set_mode(dp, &dp_display->dp_mode);
|
||||
if (rc) {
|
||||
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
return rc;
|
||||
return;
|
||||
}
|
||||
|
||||
rc = dp_display_prepare(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
return rc;
|
||||
return;
|
||||
}
|
||||
|
||||
state = dp_display->hpd_state;
|
||||
@ -1615,34 +1718,41 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
/* completed connection */
|
||||
dp_display->hpd_state = ST_CONNECTED;
|
||||
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
void dp_bridge_disable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
|
||||
struct msm_dp *dp = dp_bridge->dp_display;
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
dp_ctrl_push_idle(dp_display->ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
|
||||
struct msm_dp *dp = dp_bridge->dp_display;
|
||||
int rc = 0;
|
||||
u32 state;
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
if (dp->is_edp)
|
||||
dp_hpd_unplug_handle(dp_display, 0);
|
||||
|
||||
mutex_lock(&dp_display->event_mutex);
|
||||
|
||||
/* stop sentinel checking */
|
||||
dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT);
|
||||
state = dp_display->hpd_state;
|
||||
if (state != ST_DISCONNECT_PENDING && state != ST_CONNECTED) {
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
dp_display_disable(dp_display, 0);
|
||||
|
||||
@ -1658,14 +1768,16 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
dp_display->hpd_state = ST_DISPLAY_OFF;
|
||||
}
|
||||
|
||||
drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
|
||||
mutex_unlock(&dp_display->event_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode)
|
||||
void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
|
||||
struct msm_dp *dp = dp_bridge->dp_display;
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
@ -21,10 +21,11 @@ struct msm_dp {
|
||||
bool audio_enabled;
|
||||
bool power_on;
|
||||
unsigned int connector_type;
|
||||
bool is_edp;
|
||||
|
||||
hdmi_codec_plugged_cb plugged_cb;
|
||||
|
||||
u32 max_pclk_khz;
|
||||
bool wide_bus_en;
|
||||
|
||||
u32 max_dp_lanes;
|
||||
struct dp_audio *dp_audio;
|
||||
@ -32,9 +33,7 @@ struct msm_dp {
|
||||
|
||||
int dp_display_set_plugged_cb(struct msm_dp *dp_display,
|
||||
hdmi_codec_plugged_cb fn, struct device *codec_dev);
|
||||
int dp_display_validate_mode(struct msm_dp *dp_display, u32 mode_pclk_khz);
|
||||
int dp_display_get_modes(struct msm_dp *dp_display,
|
||||
struct dp_display_mode *dp_mode);
|
||||
int dp_display_get_modes(struct msm_dp *dp_display);
|
||||
int dp_display_request_irq(struct msm_dp *dp_display);
|
||||
bool dp_display_check_video_test(struct msm_dp *dp_display);
|
||||
int dp_display_get_test_bpp(struct msm_dp *dp_display);
|
||||
|
@ -6,40 +6,25 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_kms.h"
|
||||
#include "dp_drm.h"
|
||||
|
||||
|
||||
struct msm_dp_bridge {
|
||||
struct drm_bridge bridge;
|
||||
struct msm_dp *dp_display;
|
||||
};
|
||||
|
||||
#define to_dp_display(x) container_of((x), struct msm_dp_bridge, bridge)
|
||||
|
||||
struct dp_connector {
|
||||
struct drm_connector base;
|
||||
struct msm_dp *dp_display;
|
||||
};
|
||||
#define to_dp_connector(x) container_of(x, struct dp_connector, base)
|
||||
|
||||
/**
|
||||
* dp_connector_detect - callback to determine if connector is connected
|
||||
* @conn: Pointer to drm connector structure
|
||||
* @force: Force detect setting from drm framework
|
||||
* Returns: Connector 'is connected' status
|
||||
* dp_bridge_detect - callback to determine if connector is connected
|
||||
* @bridge: Pointer to drm bridge structure
|
||||
* Returns: Bridge's 'is connected' status
|
||||
*/
|
||||
static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
|
||||
bool force)
|
||||
static enum drm_connector_status dp_bridge_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct msm_dp *dp;
|
||||
|
||||
dp = to_dp_connector(conn)->dp_display;
|
||||
dp = to_dp_bridge(bridge)->dp_display;
|
||||
|
||||
DRM_DEBUG_DP("is_connected = %s\n",
|
||||
drm_dbg_dp(dp->drm_dev, "is_connected = %s\n",
|
||||
(dp->is_connected) ? "true" : "false");
|
||||
|
||||
return (dp->is_connected) ? connector_status_connected :
|
||||
@ -47,173 +32,45 @@ static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
|
||||
* dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add()
|
||||
* @bridge: Poiner to drm bridge
|
||||
* @connector: Pointer to drm connector structure
|
||||
* Returns: Number of modes added
|
||||
*/
|
||||
static int dp_connector_get_modes(struct drm_connector *connector)
|
||||
static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector)
|
||||
{
|
||||
int rc = 0;
|
||||
struct msm_dp *dp;
|
||||
struct dp_display_mode *dp_mode = NULL;
|
||||
struct drm_display_mode *m, drm_mode;
|
||||
|
||||
if (!connector)
|
||||
return 0;
|
||||
|
||||
dp = to_dp_connector(connector)->dp_display;
|
||||
|
||||
dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL);
|
||||
if (!dp_mode)
|
||||
return 0;
|
||||
dp = to_dp_bridge(bridge)->dp_display;
|
||||
|
||||
/* pluggable case assumes EDID is read when HPD */
|
||||
if (dp->is_connected) {
|
||||
/*
|
||||
*The get_modes() function might return one mode that is stored
|
||||
* in dp_mode when compliance test is in progress. If not, the
|
||||
* return value is equal to the total number of modes supported
|
||||
* by the sink
|
||||
*/
|
||||
rc = dp_display_get_modes(dp, dp_mode);
|
||||
rc = dp_display_get_modes(dp);
|
||||
if (rc <= 0) {
|
||||
DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
|
||||
kfree(dp_mode);
|
||||
return rc;
|
||||
}
|
||||
if (dp_mode->drm_mode.clock) { /* valid DP mode */
|
||||
memset(&drm_mode, 0x0, sizeof(drm_mode));
|
||||
drm_mode_copy(&drm_mode, &dp_mode->drm_mode);
|
||||
m = drm_mode_duplicate(connector->dev, &drm_mode);
|
||||
if (!m) {
|
||||
DRM_ERROR("failed to add mode %ux%u\n",
|
||||
drm_mode.hdisplay,
|
||||
drm_mode.vdisplay);
|
||||
kfree(dp_mode);
|
||||
return 0;
|
||||
}
|
||||
drm_mode_probed_add(connector, m);
|
||||
}
|
||||
} else {
|
||||
DRM_DEBUG_DP("No sink connected\n");
|
||||
drm_dbg_dp(connector->dev, "No sink connected\n");
|
||||
}
|
||||
kfree(dp_mode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_connector_mode_valid - callback to determine if specified mode is valid
|
||||
* @connector: Pointer to drm connector structure
|
||||
* @mode: Pointer to drm mode structure
|
||||
* Returns: Validity status for specified mode
|
||||
*/
|
||||
static enum drm_mode_status dp_connector_mode_valid(
|
||||
struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct msm_dp *dp_disp;
|
||||
|
||||
dp_disp = to_dp_connector(connector)->dp_display;
|
||||
|
||||
if ((dp_disp->max_pclk_khz <= 0) ||
|
||||
(dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) ||
|
||||
(mode->clock > dp_disp->max_pclk_khz))
|
||||
return MODE_BAD;
|
||||
|
||||
return dp_display_validate_mode(dp_disp, mode->clock);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dp_connector_funcs = {
|
||||
.detect = dp_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs dp_connector_helper_funcs = {
|
||||
.get_modes = dp_connector_get_modes,
|
||||
.mode_valid = dp_connector_mode_valid,
|
||||
};
|
||||
|
||||
/* connector initialization */
|
||||
struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct dp_connector *dp_connector;
|
||||
int ret;
|
||||
|
||||
dp_connector = devm_kzalloc(dp_display->drm_dev->dev,
|
||||
sizeof(*dp_connector),
|
||||
GFP_KERNEL);
|
||||
if (!dp_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dp_connector->dp_display = dp_display;
|
||||
|
||||
connector = &dp_connector->base;
|
||||
|
||||
ret = drm_connector_init(dp_display->drm_dev, connector,
|
||||
&dp_connector_funcs,
|
||||
dp_display->connector_type);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_connector_helper_add(connector, &dp_connector_helper_funcs);
|
||||
|
||||
/*
|
||||
* Enable HPD to let hpd event is handled when cable is connected.
|
||||
*/
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
drm_connector_attach_encoder(connector, dp_display->encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
|
||||
struct msm_dp *dp_display = dp_bridge->dp_display;
|
||||
|
||||
msm_dp_display_mode_set(dp_display, drm_bridge->encoder, mode, adjusted_mode);
|
||||
}
|
||||
|
||||
static void dp_bridge_enable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
|
||||
struct msm_dp *dp_display = dp_bridge->dp_display;
|
||||
|
||||
msm_dp_display_enable(dp_display, drm_bridge->encoder);
|
||||
}
|
||||
|
||||
static void dp_bridge_disable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
|
||||
struct msm_dp *dp_display = dp_bridge->dp_display;
|
||||
|
||||
msm_dp_display_pre_disable(dp_display, drm_bridge->encoder);
|
||||
}
|
||||
|
||||
static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
|
||||
{
|
||||
struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
|
||||
struct msm_dp *dp_display = dp_bridge->dp_display;
|
||||
|
||||
msm_dp_display_disable(dp_display, drm_bridge->encoder);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs dp_bridge_ops = {
|
||||
.enable = dp_bridge_enable,
|
||||
.disable = dp_bridge_disable,
|
||||
.post_disable = dp_bridge_post_disable,
|
||||
.mode_set = dp_bridge_mode_set,
|
||||
.mode_valid = dp_bridge_mode_valid,
|
||||
.get_modes = dp_bridge_get_modes,
|
||||
.detect = dp_bridge_detect,
|
||||
};
|
||||
|
||||
struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
int rc;
|
||||
@ -228,11 +85,33 @@ struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_devi
|
||||
|
||||
bridge = &dp_bridge->bridge;
|
||||
bridge->funcs = &dp_bridge_ops;
|
||||
bridge->encoder = encoder;
|
||||
bridge->type = dp_display->connector_type;
|
||||
|
||||
/*
|
||||
* Many ops only make sense for DP. Why?
|
||||
* - Detect/HPD are used by DRM to know if a display is _physically_
|
||||
* there, not whether the display is powered on / finished initting.
|
||||
* On eDP we assume the display is always there because you can't
|
||||
* know until power is applied. If we don't implement the ops DRM will
|
||||
* assume our display is always there.
|
||||
* - Currently eDP mode reading is driven by the panel driver. This
|
||||
* allows the panel driver to properly power itself on to read the
|
||||
* modes.
|
||||
*/
|
||||
if (!dp_display->is_edp) {
|
||||
bridge->ops =
|
||||
DRM_BRIDGE_OP_DETECT |
|
||||
DRM_BRIDGE_OP_HPD |
|
||||
DRM_BRIDGE_OP_MODES;
|
||||
}
|
||||
|
||||
drm_bridge_add(bridge);
|
||||
|
||||
rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
|
||||
drm_bridge_remove(bridge);
|
||||
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
@ -249,3 +128,17 @@ struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_devi
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
/* connector initialization */
|
||||
struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
|
||||
connector = drm_bridge_connector_init(dp_display->drm_dev, dp_display->encoder);
|
||||
if (IS_ERR(connector))
|
||||
return connector;
|
||||
|
||||
drm_connector_attach_encoder(connector, dp_display->encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
@ -7,12 +7,30 @@
|
||||
#define _DP_DRM_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "dp_display.h"
|
||||
|
||||
struct msm_dp_bridge {
|
||||
struct drm_bridge bridge;
|
||||
struct msm_dp *dp_display;
|
||||
};
|
||||
|
||||
#define to_dp_bridge(x) container_of((x), struct msm_dp_bridge, bridge)
|
||||
|
||||
struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display);
|
||||
struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
|
||||
void dp_bridge_enable(struct drm_bridge *drm_bridge);
|
||||
void dp_bridge_disable(struct drm_bridge *drm_bridge);
|
||||
void dp_bridge_post_disable(struct drm_bridge *drm_bridge);
|
||||
enum drm_mode_status dp_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode);
|
||||
void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode);
|
||||
|
||||
#endif /* _DP_DRM_H_ */
|
||||
|
@ -36,6 +36,7 @@ struct dp_link_request {
|
||||
struct dp_link_private {
|
||||
u32 prev_sink_count;
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_link dp_link;
|
||||
|
||||
@ -128,14 +129,14 @@ static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_1 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_1 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_1 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH2);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_2 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_2 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_2 = 0x%x\n", ret);
|
||||
|
||||
/* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH3);
|
||||
@ -143,42 +144,42 @@ static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_3 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_3 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_3 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH4);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_4 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_4 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_4 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH5);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_5 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_5 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_5 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH6);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_6 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_6 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_6 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH7);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_7 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_7 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_7 = 0x%x\n", ret);
|
||||
|
||||
ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH8);
|
||||
if (ret == -EINVAL)
|
||||
goto exit;
|
||||
|
||||
req->test_audio_period_ch_8 = ret;
|
||||
DRM_DEBUG_DP("test_audio_period_ch_8 = 0x%x\n", ret);
|
||||
drm_dbg_dp(link->drm_dev, "test_audio_period_ch_8 = 0x%x\n", ret);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@ -205,7 +206,7 @@ static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
|
||||
}
|
||||
|
||||
link->dp_link.test_audio.test_audio_pattern_type = data;
|
||||
DRM_DEBUG_DP("audio pattern type = 0x%x\n", data);
|
||||
drm_dbg_dp(link->drm_dev, "audio pattern type = 0x%x\n", data);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@ -246,8 +247,9 @@ static int dp_link_parse_audio_mode(struct dp_link_private *link)
|
||||
|
||||
link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate;
|
||||
link->dp_link.test_audio.test_audio_channel_count = channel_count;
|
||||
DRM_DEBUG_DP("sampling_rate = 0x%x, channel_count = 0x%x\n",
|
||||
sampling_rate, channel_count);
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"sampling_rate = 0x%x, channel_count = 0x%x\n",
|
||||
sampling_rate, channel_count);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@ -486,7 +488,8 @@ static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("link video pattern = 0x%x\n"
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"link video pattern = 0x%x\n"
|
||||
"link dynamic range = 0x%x\n"
|
||||
"link bit depth = 0x%x\n"
|
||||
"TEST_H_TOTAL = %d, TEST_V_TOTAL = %d\n"
|
||||
@ -543,7 +546,8 @@ static int dp_link_parse_link_training_params(struct dp_link_private *link)
|
||||
}
|
||||
|
||||
link->request.test_link_rate = bp;
|
||||
DRM_DEBUG_DP("link rate = 0x%x\n", link->request.test_link_rate);
|
||||
drm_dbg_dp(link->drm_dev, "link rate = 0x%x\n",
|
||||
link->request.test_link_rate);
|
||||
|
||||
rlen = drm_dp_dpcd_readb(link->aux, DP_TEST_LANE_COUNT, &bp);
|
||||
if (rlen < 0) {
|
||||
@ -558,7 +562,8 @@ static int dp_link_parse_link_training_params(struct dp_link_private *link)
|
||||
}
|
||||
|
||||
link->request.test_lane_count = bp;
|
||||
DRM_DEBUG_DP("lane count = 0x%x\n", link->request.test_lane_count);
|
||||
drm_dbg_dp(link->drm_dev, "lane count = 0x%x\n",
|
||||
link->request.test_lane_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -583,7 +588,7 @@ static int dp_link_parse_phy_test_params(struct dp_link_private *link)
|
||||
|
||||
link->dp_link.phy_params.phy_test_pattern_sel = data & 0x07;
|
||||
|
||||
DRM_DEBUG_DP("phy_test_pattern_sel = 0x%x\n", data);
|
||||
drm_dbg_dp(link->drm_dev, "phy_test_pattern_sel = 0x%x\n", data);
|
||||
|
||||
switch (data) {
|
||||
case DP_PHY_TEST_PATTERN_SEL_MASK:
|
||||
@ -639,10 +644,10 @@ static int dp_link_parse_request(struct dp_link_private *link)
|
||||
return rlen;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("device service irq vector = 0x%x\n", data);
|
||||
drm_dbg_dp(link->drm_dev, "device service irq vector = 0x%x\n", data);
|
||||
|
||||
if (!(data & DP_AUTOMATED_TEST_REQUEST)) {
|
||||
DRM_DEBUG_DP("no test requested\n");
|
||||
drm_dbg_dp(link->drm_dev, "no test requested\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -657,11 +662,11 @@ static int dp_link_parse_request(struct dp_link_private *link)
|
||||
}
|
||||
|
||||
if (!data || (data == DP_TEST_LINK_FAUX_PATTERN)) {
|
||||
DRM_DEBUG_DP("link 0x%x not supported\n", data);
|
||||
drm_dbg_dp(link->drm_dev, "link 0x%x not supported\n", data);
|
||||
goto end;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("Test:(0x%x) requested\n", data);
|
||||
drm_dbg_dp(link->drm_dev, "Test:(0x%x) requested\n", data);
|
||||
link->request.test_requested = data;
|
||||
if (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) {
|
||||
ret = dp_link_parse_phy_test_params(link);
|
||||
@ -732,8 +737,8 @@ static int dp_link_parse_sink_count(struct dp_link *dp_link)
|
||||
link->dp_link.sink_count =
|
||||
DP_GET_SINK_COUNT(link->dp_link.sink_count);
|
||||
|
||||
DRM_DEBUG_DP("sink_count = 0x%x, cp_ready = 0x%x\n",
|
||||
link->dp_link.sink_count, cp_ready);
|
||||
drm_dbg_dp(link->drm_dev, "sink_count = 0x%x, cp_ready = 0x%x\n",
|
||||
link->dp_link.sink_count, cp_ready);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -774,7 +779,8 @@ static int dp_link_process_link_training_request(struct dp_link_private *link)
|
||||
if (link->request.test_requested != DP_TEST_LINK_TRAINING)
|
||||
return -EINVAL;
|
||||
|
||||
DRM_DEBUG_DP("Test:0x%x link rate = 0x%x, lane count = 0x%x\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Test:0x%x link rate = 0x%x, lane count = 0x%x\n",
|
||||
DP_TEST_LINK_TRAINING,
|
||||
link->request.test_link_rate,
|
||||
link->request.test_lane_count);
|
||||
@ -852,13 +858,13 @@ bool dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum)
|
||||
|
||||
static void dp_link_parse_vx_px(struct dp_link_private *link)
|
||||
{
|
||||
DRM_DEBUG_DP("vx: 0=%d, 1=%d, 2=%d, 3=%d\n",
|
||||
drm_dbg_dp(link->drm_dev, "vx: 0=%d, 1=%d, 2=%d, 3=%d\n",
|
||||
drm_dp_get_adjust_request_voltage(link->link_status, 0),
|
||||
drm_dp_get_adjust_request_voltage(link->link_status, 1),
|
||||
drm_dp_get_adjust_request_voltage(link->link_status, 2),
|
||||
drm_dp_get_adjust_request_voltage(link->link_status, 3));
|
||||
|
||||
DRM_DEBUG_DP("px: 0=%d, 1=%d, 2=%d, 3=%d\n",
|
||||
drm_dbg_dp(link->drm_dev, "px: 0=%d, 1=%d, 2=%d, 3=%d\n",
|
||||
drm_dp_get_adjust_request_pre_emphasis(link->link_status, 0),
|
||||
drm_dp_get_adjust_request_pre_emphasis(link->link_status, 1),
|
||||
drm_dp_get_adjust_request_pre_emphasis(link->link_status, 2),
|
||||
@ -868,7 +874,8 @@ static void dp_link_parse_vx_px(struct dp_link_private *link)
|
||||
* Update the voltage and pre-emphasis levels as per DPCD request
|
||||
* vector.
|
||||
*/
|
||||
DRM_DEBUG_DP("Current: v_level = 0x%x, p_level = 0x%x\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Current: v_level = 0x%x, p_level = 0x%x\n",
|
||||
link->dp_link.phy_params.v_level,
|
||||
link->dp_link.phy_params.p_level);
|
||||
link->dp_link.phy_params.v_level =
|
||||
@ -878,7 +885,8 @@ static void dp_link_parse_vx_px(struct dp_link_private *link)
|
||||
|
||||
link->dp_link.phy_params.p_level >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
|
||||
|
||||
DRM_DEBUG_DP("Requested: v_level = 0x%x, p_level = 0x%x\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Requested: v_level = 0x%x, p_level = 0x%x\n",
|
||||
link->dp_link.phy_params.v_level,
|
||||
link->dp_link.phy_params.p_level);
|
||||
}
|
||||
@ -895,7 +903,7 @@ static int dp_link_process_phy_test_pattern_request(
|
||||
struct dp_link_private *link)
|
||||
{
|
||||
if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) {
|
||||
DRM_DEBUG_DP("no phy test\n");
|
||||
drm_dbg_dp(link->drm_dev, "no phy test\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -907,11 +915,13 @@ static int dp_link_process_phy_test_pattern_request(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("Current: rate = 0x%x, lane count = 0x%x\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Current: rate = 0x%x, lane count = 0x%x\n",
|
||||
link->dp_link.link_params.rate,
|
||||
link->dp_link.link_params.num_lanes);
|
||||
|
||||
DRM_DEBUG_DP("Requested: rate = 0x%x, lane count = 0x%x\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Requested: rate = 0x%x, lane count = 0x%x\n",
|
||||
link->request.test_link_rate,
|
||||
link->request.test_lane_count);
|
||||
|
||||
@ -942,17 +952,18 @@ static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
|
||||
*/
|
||||
static int dp_link_process_link_status_update(struct dp_link_private *link)
|
||||
{
|
||||
bool channel_eq_done = drm_dp_channel_eq_ok(link->link_status,
|
||||
link->dp_link.link_params.num_lanes);
|
||||
bool channel_eq_done = drm_dp_channel_eq_ok(link->link_status,
|
||||
link->dp_link.link_params.num_lanes);
|
||||
|
||||
bool clock_recovery_done = drm_dp_clock_recovery_ok(link->link_status,
|
||||
link->dp_link.link_params.num_lanes);
|
||||
bool clock_recovery_done = drm_dp_clock_recovery_ok(link->link_status,
|
||||
link->dp_link.link_params.num_lanes);
|
||||
|
||||
DRM_DEBUG_DP("channel_eq_done = %d, clock_recovery_done = %d\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"channel_eq_done = %d, clock_recovery_done = %d\n",
|
||||
channel_eq_done, clock_recovery_done);
|
||||
|
||||
if (channel_eq_done && clock_recovery_done)
|
||||
return -EINVAL;
|
||||
if (channel_eq_done && clock_recovery_done)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
return 0;
|
||||
@ -1058,7 +1069,8 @@ int dp_link_process_request(struct dp_link *dp_link)
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("sink request=%#x", dp_link->sink_request);
|
||||
drm_dbg_dp(link->drm_dev, "sink request=%#x",
|
||||
dp_link->sink_request);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1090,18 +1102,22 @@ int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
|
||||
{
|
||||
int i;
|
||||
int v_max = 0, p_max = 0;
|
||||
struct dp_link_private *link;
|
||||
|
||||
if (!dp_link) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
link = container_of(dp_link, struct dp_link_private, dp_link);
|
||||
|
||||
/* use the max level across lanes */
|
||||
for (i = 0; i < dp_link->link_params.num_lanes; i++) {
|
||||
u8 data_v = drm_dp_get_adjust_request_voltage(link_status, i);
|
||||
u8 data_p = drm_dp_get_adjust_request_pre_emphasis(link_status,
|
||||
i);
|
||||
DRM_DEBUG_DP("lane=%d req_vol_swing=%d req_pre_emphasis=%d\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"lane=%d req_vol_swing=%d req_pre_emphasis=%d\n",
|
||||
i, data_v, data_p);
|
||||
if (v_max < data_v)
|
||||
v_max = data_v;
|
||||
@ -1117,14 +1133,16 @@ int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
|
||||
* the allowable range.
|
||||
*/
|
||||
if (dp_link->phy_params.v_level > DP_TRAIN_VOLTAGE_SWING_MAX) {
|
||||
DRM_DEBUG_DP("Requested vSwingLevel=%d, change to %d\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Requested vSwingLevel=%d, change to %d\n",
|
||||
dp_link->phy_params.v_level,
|
||||
DP_TRAIN_VOLTAGE_SWING_MAX);
|
||||
dp_link->phy_params.v_level = DP_TRAIN_VOLTAGE_SWING_MAX;
|
||||
}
|
||||
|
||||
if (dp_link->phy_params.p_level > DP_TRAIN_PRE_EMPHASIS_MAX) {
|
||||
DRM_DEBUG_DP("Requested preEmphasisLevel=%d, change to %d\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Requested preEmphasisLevel=%d, change to %d\n",
|
||||
dp_link->phy_params.p_level,
|
||||
DP_TRAIN_PRE_EMPHASIS_MAX);
|
||||
dp_link->phy_params.p_level = DP_TRAIN_PRE_EMPHASIS_MAX;
|
||||
@ -1133,13 +1151,14 @@ int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
|
||||
if ((dp_link->phy_params.p_level > DP_TRAIN_PRE_EMPHASIS_LVL_1)
|
||||
&& (dp_link->phy_params.v_level ==
|
||||
DP_TRAIN_VOLTAGE_SWING_LVL_2)) {
|
||||
DRM_DEBUG_DP("Requested preEmphasisLevel=%d, change to %d\n",
|
||||
drm_dbg_dp(link->drm_dev,
|
||||
"Requested preEmphasisLevel=%d, change to %d\n",
|
||||
dp_link->phy_params.p_level,
|
||||
DP_TRAIN_PRE_EMPHASIS_LVL_1);
|
||||
dp_link->phy_params.p_level = DP_TRAIN_PRE_EMPHASIS_LVL_1;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("adjusted: v_level=%d, p_level=%d\n",
|
||||
drm_dbg_dp(link->drm_dev, "adjusted: v_level=%d, p_level=%d\n",
|
||||
dp_link->phy_params.v_level, dp_link->phy_params.p_level);
|
||||
|
||||
return 0;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
struct dp_panel_private {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct dp_panel dp_panel;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_link *link;
|
||||
@ -50,7 +51,8 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
|
||||
|
||||
/* check for EXTENDED_RECEIVER_CAPABILITY_FIELD_PRESENT */
|
||||
if (temp & BIT(7)) {
|
||||
DRM_DEBUG_DP("using EXTENDED_RECEIVER_CAPABILITY_FIELD\n");
|
||||
drm_dbg_dp(panel->drm_dev,
|
||||
"using EXTENDED_RECEIVER_CAPABILITY_FIELD\n");
|
||||
offset = DPRX_EXTENDED_DPCD_FIELD;
|
||||
}
|
||||
|
||||
@ -80,9 +82,9 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
|
||||
if (link_info->rate >= (drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4)))
|
||||
link_info->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4);
|
||||
|
||||
DRM_DEBUG_DP("version: %d.%d\n", major, minor);
|
||||
DRM_DEBUG_DP("link_rate=%d\n", link_info->rate);
|
||||
DRM_DEBUG_DP("lane_count=%d\n", link_info->num_lanes);
|
||||
drm_dbg_dp(panel->drm_dev, "version: %d.%d\n", major, minor);
|
||||
drm_dbg_dp(panel->drm_dev, "link_rate=%d\n", link_info->rate);
|
||||
drm_dbg_dp(panel->drm_dev, "lane_count=%d\n", link_info->num_lanes);
|
||||
|
||||
if (drm_dp_enhanced_frame_cap(dpcd))
|
||||
link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
|
||||
@ -220,7 +222,8 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
|
||||
}
|
||||
|
||||
if (panel->aux_cfg_update_done) {
|
||||
DRM_DEBUG_DP("read DPCD with updated AUX config\n");
|
||||
drm_dbg_dp(panel->drm_dev,
|
||||
"read DPCD with updated AUX config\n");
|
||||
rc = dp_panel_read_dpcd(dp_panel);
|
||||
bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate);
|
||||
if (rc || !is_link_rate_valid(bw_code) ||
|
||||
@ -259,7 +262,7 @@ u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
|
||||
}
|
||||
|
||||
int dp_panel_get_modes(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector, struct dp_display_mode *mode)
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
if (!dp_panel) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
@ -334,7 +337,8 @@ void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
|
||||
catalog = panel->catalog;
|
||||
|
||||
if (!panel->panel_on) {
|
||||
DRM_DEBUG_DP("DP panel not enabled, handle TPG on next on\n");
|
||||
drm_dbg_dp(panel->drm_dev,
|
||||
"DP panel not enabled, handle TPG on next on\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -343,7 +347,7 @@ void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("%s: calling catalog tpg_enable\n", __func__);
|
||||
drm_dbg_dp(panel->drm_dev, "calling catalog tpg_enable\n");
|
||||
dp_catalog_panel_tpg_enable(catalog, &panel->dp_panel.dp_mode.drm_mode);
|
||||
}
|
||||
|
||||
@ -369,12 +373,12 @@ int dp_panel_timing_cfg(struct dp_panel *dp_panel)
|
||||
catalog = panel->catalog;
|
||||
drm_mode = &panel->dp_panel.dp_mode.drm_mode;
|
||||
|
||||
DRM_DEBUG_DP("width=%d hporch= %d %d %d\n",
|
||||
drm_dbg_dp(panel->drm_dev, "width=%d hporch= %d %d %d\n",
|
||||
drm_mode->hdisplay, drm_mode->htotal - drm_mode->hsync_end,
|
||||
drm_mode->hsync_start - drm_mode->hdisplay,
|
||||
drm_mode->hsync_end - drm_mode->hsync_start);
|
||||
|
||||
DRM_DEBUG_DP("height=%d vporch= %d %d %d\n",
|
||||
drm_dbg_dp(panel->drm_dev, "height=%d vporch= %d %d %d\n",
|
||||
drm_mode->vdisplay, drm_mode->vtotal - drm_mode->vsync_end,
|
||||
drm_mode->vsync_start - drm_mode->vdisplay,
|
||||
drm_mode->vsync_end - drm_mode->vsync_start);
|
||||
@ -418,30 +422,37 @@ int dp_panel_timing_cfg(struct dp_panel *dp_panel)
|
||||
int dp_panel_init_panel_info(struct dp_panel *dp_panel)
|
||||
{
|
||||
struct drm_display_mode *drm_mode;
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
drm_mode = &dp_panel->dp_mode.drm_mode;
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
|
||||
/*
|
||||
* print resolution info as this is a result
|
||||
* of user initiated action of cable connection
|
||||
*/
|
||||
DRM_DEBUG_DP("SET NEW RESOLUTION:\n");
|
||||
DRM_DEBUG_DP("%dx%d@%dfps\n", drm_mode->hdisplay,
|
||||
drm_mode->vdisplay, drm_mode_vrefresh(drm_mode));
|
||||
DRM_DEBUG_DP("h_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_dbg_dp(panel->drm_dev, "SET NEW RESOLUTION:\n");
|
||||
drm_dbg_dp(panel->drm_dev, "%dx%d@%dfps\n",
|
||||
drm_mode->hdisplay, drm_mode->vdisplay, drm_mode_vrefresh(drm_mode));
|
||||
drm_dbg_dp(panel->drm_dev,
|
||||
"h_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_mode->htotal - drm_mode->hsync_end,
|
||||
drm_mode->hsync_start - drm_mode->hdisplay,
|
||||
drm_mode->hsync_end - drm_mode->hsync_start);
|
||||
DRM_DEBUG_DP("v_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_dbg_dp(panel->drm_dev,
|
||||
"v_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_mode->vtotal - drm_mode->vsync_end,
|
||||
drm_mode->vsync_start - drm_mode->vdisplay,
|
||||
drm_mode->vsync_end - drm_mode->vsync_start);
|
||||
DRM_DEBUG_DP("pixel clock (KHz)=(%d)\n", drm_mode->clock);
|
||||
DRM_DEBUG_DP("bpp = %d\n", dp_panel->dp_mode.bpp);
|
||||
drm_dbg_dp(panel->drm_dev, "pixel clock (KHz)=(%d)\n",
|
||||
drm_mode->clock);
|
||||
drm_dbg_dp(panel->drm_dev, "bpp = %d\n", dp_panel->dp_mode.bpp);
|
||||
|
||||
dp_panel->dp_mode.bpp = max_t(u32, 18,
|
||||
min_t(u32, dp_panel->dp_mode.bpp, 30));
|
||||
DRM_DEBUG_DP("updated bpp = %d\n", dp_panel->dp_mode.bpp);
|
||||
min_t(u32, dp_panel->dp_mode.bpp, 30));
|
||||
drm_dbg_dp(panel->drm_dev, "updated bpp = %d\n",
|
||||
dp_panel->dp_mode.bpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ struct dp_panel {
|
||||
bool video_test;
|
||||
|
||||
u32 vic;
|
||||
u32 max_pclk_khz;
|
||||
u32 max_dp_lanes;
|
||||
|
||||
u32 max_bw_code;
|
||||
@ -65,7 +64,7 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
|
||||
u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel, u32 mode_max_bpp,
|
||||
u32 mode_pclk_khz);
|
||||
int dp_panel_get_modes(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector, struct dp_display_mode *mode);
|
||||
struct drm_connector *connector);
|
||||
void dp_panel_handle_sink_request(struct dp_panel *dp_panel);
|
||||
void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable);
|
||||
|
||||
|
@ -260,12 +260,10 @@ static int dp_parser_clock(struct dp_parser *parser)
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("clock parsing successful\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_find_next_bridge(struct dp_parser *parser)
|
||||
int dp_parser_find_next_bridge(struct dp_parser *parser)
|
||||
{
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
struct drm_bridge *bridge;
|
||||
@ -279,7 +277,7 @@ static int dp_parser_find_next_bridge(struct dp_parser *parser)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_parse(struct dp_parser *parser, int connector_type)
|
||||
static int dp_parser_parse(struct dp_parser *parser)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
@ -300,25 +298,6 @@ static int dp_parser_parse(struct dp_parser *parser, int connector_type)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* External bridges are mandatory for eDP interfaces: one has to
|
||||
* provide at least an eDP panel (which gets wrapped into panel-bridge).
|
||||
*
|
||||
* For DisplayPort interfaces external bridges are optional, so
|
||||
* silently ignore an error if one is not present (-ENODEV).
|
||||
*/
|
||||
rc = dp_parser_find_next_bridge(parser);
|
||||
if (rc == -ENODEV) {
|
||||
if (connector_type == DRM_MODE_CONNECTOR_eDP) {
|
||||
DRM_ERROR("eDP: next bridge is not present\n");
|
||||
return rc;
|
||||
}
|
||||
} else if (rc) {
|
||||
if (rc != -EPROBE_DEFER)
|
||||
DRM_ERROR("DP: error parsing next bridge: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Map the corresponding regulator information according to
|
||||
* version. Currently, since we only have one supported platform,
|
||||
* mapping the regulator directly.
|
||||
|
@ -125,7 +125,7 @@ struct dp_parser {
|
||||
u32 max_dp_lanes;
|
||||
struct drm_bridge *next_bridge;
|
||||
|
||||
int (*parse)(struct dp_parser *parser, int connector_type);
|
||||
int (*parse)(struct dp_parser *parser);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -141,4 +141,16 @@ struct dp_parser {
|
||||
*/
|
||||
struct dp_parser *dp_parser_get(struct platform_device *pdev);
|
||||
|
||||
/**
|
||||
* dp_parser_find_next_bridge() - find an additional bridge to DP
|
||||
*
|
||||
* @parser: dp_parser data from client
|
||||
*
|
||||
* This function is used to find any additional bridge attached to
|
||||
* the DP controller. The eDP interface requires a panel bridge.
|
||||
*
|
||||
* Return: 0 if able to get the bridge, otherwise negative errno for failure.
|
||||
*/
|
||||
int dp_parser_find_next_bridge(struct dp_parser *parser);
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@ struct dp_power_private {
|
||||
struct dp_parser *parser;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct clk *link_clk_src;
|
||||
struct clk *pixel_provider;
|
||||
struct clk *link_provider;
|
||||
@ -208,7 +209,12 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
|
||||
|
||||
int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type)
|
||||
{
|
||||
DRM_DEBUG_DP("core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
|
||||
struct dp_power_private *power;
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
|
||||
dp_power->core_clks_on, dp_power->link_clks_on, dp_power->stream_clks_on);
|
||||
|
||||
if (pm_type == DP_CORE_PM)
|
||||
@ -240,22 +246,26 @@ int dp_power_clk_enable(struct dp_power *dp_power,
|
||||
|
||||
if (enable) {
|
||||
if (pm_type == DP_CORE_PM && dp_power->core_clks_on) {
|
||||
DRM_DEBUG_DP("core clks already enabled\n");
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"core clks already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) {
|
||||
DRM_DEBUG_DP("links clks already enabled\n");
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"links clks already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pm_type == DP_STREAM_PM && dp_power->stream_clks_on) {
|
||||
DRM_DEBUG_DP("pixel clks already enabled\n");
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"pixel clks already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) {
|
||||
DRM_DEBUG_DP("Enable core clks before link clks\n");
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"Enable core clks before link clks\n");
|
||||
|
||||
rc = dp_power_clk_set_rate(power, DP_CORE_PM, enable);
|
||||
if (rc) {
|
||||
@ -282,10 +292,11 @@ int dp_power_clk_enable(struct dp_power *dp_power,
|
||||
else
|
||||
dp_power->link_clks_on = enable;
|
||||
|
||||
DRM_DEBUG_DP("%s clocks for %s\n",
|
||||
drm_dbg_dp(power->drm_dev, "%s clocks for %s\n",
|
||||
enable ? "enable" : "disable",
|
||||
dp_parser_pm_name(pm_type));
|
||||
DRM_DEBUG_DP("strem_clks:%s link_clks:%s core_clks:%s\n",
|
||||
drm_dbg_dp(power->drm_dev,
|
||||
"strem_clks:%s link_clks:%s core_clks:%s\n",
|
||||
dp_power->stream_clks_on ? "on" : "off",
|
||||
dp_power->link_clks_on ? "on" : "off",
|
||||
dp_power->core_clks_on ? "on" : "off");
|
||||
|
@ -21,6 +21,11 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi)
|
||||
return !(host_flags & MIPI_DSI_MODE_VIDEO);
|
||||
}
|
||||
|
||||
struct msm_display_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
return msm_dsi_host_get_dsc_config(msm_dsi->host);
|
||||
}
|
||||
|
||||
static int dsi_get_phy(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
struct platform_device *pdev = msm_dsi->pdev;
|
||||
@ -273,7 +278,6 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
|
||||
}
|
||||
|
||||
priv->bridges[priv->num_bridges++] = msm_dsi->bridge;
|
||||
priv->connectors[priv->num_connectors++] = msm_dsi->connector;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
@ -114,6 +114,8 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host,
|
||||
int msm_dsi_host_power_off(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host,
|
||||
const struct drm_display_mode *mode);
|
||||
enum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host,
|
||||
const struct drm_display_mode *mode);
|
||||
struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host);
|
||||
unsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host);
|
||||
struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host);
|
||||
@ -152,6 +154,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
|
||||
int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
|
||||
void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host);
|
||||
void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host);
|
||||
struct msm_display_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
|
||||
|
||||
/* dsi phy */
|
||||
struct msm_dsi_phy;
|
||||
|
@ -704,5 +704,85 @@ static inline uint32_t DSI_VERSION_MAJOR(uint32_t val)
|
||||
|
||||
#define REG_DSI_CPHY_MODE_CTRL 0x000002d4
|
||||
|
||||
#define REG_DSI_VIDEO_COMPRESSION_MODE_CTRL 0x0000029c
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_WC__MASK 0xffff0000
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_WC__SHIFT 16
|
||||
static inline uint32_t DSI_VIDEO_COMPRESSION_MODE_CTRL_WC(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VIDEO_COMPRESSION_MODE_CTRL_WC__SHIFT) & DSI_VIDEO_COMPRESSION_MODE_CTRL_WC__MASK;
|
||||
}
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE__MASK 0x00003f00
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE__SHIFT 8
|
||||
static inline uint32_t DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE__SHIFT) & DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE__MASK;
|
||||
}
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE__MASK 0x000000c0
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE__SHIFT 6
|
||||
static inline uint32_t DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE__SHIFT) & DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE__MASK;
|
||||
}
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM__MASK 0x00000030
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM__SHIFT 4
|
||||
static inline uint32_t DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM__SHIFT) & DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM__MASK;
|
||||
}
|
||||
#define DSI_VIDEO_COMPRESSION_MODE_CTRL_EN 0x00000001
|
||||
|
||||
#define REG_DSI_COMMAND_COMPRESSION_MODE_CTRL 0x000002a4
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_DATATYPE__MASK 0x3f000000
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_DATATYPE__SHIFT 24
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_DATATYPE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_DATATYPE__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_DATATYPE__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_PKT_PER_LINE__MASK 0x00c00000
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_PKT_PER_LINE__SHIFT 22
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_PKT_PER_LINE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_PKT_PER_LINE__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_PKT_PER_LINE__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EOL_BYTE_NUM__MASK 0x00300000
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EOL_BYTE_NUM__SHIFT 20
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EOL_BYTE_NUM(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EOL_BYTE_NUM__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EOL_BYTE_NUM__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM1_EN 0x00010000
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE__MASK 0x00003f00
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE__SHIFT 8
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_PKT_PER_LINE__MASK 0x000000c0
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_PKT_PER_LINE__SHIFT 6
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_PKT_PER_LINE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_PKT_PER_LINE__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_PKT_PER_LINE__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EOL_BYTE_NUM__MASK 0x00000030
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EOL_BYTE_NUM__SHIFT 4
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EOL_BYTE_NUM(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EOL_BYTE_NUM__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EOL_BYTE_NUM__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_EN 0x00000001
|
||||
|
||||
#define REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2 0x000002a8
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM1_SLICE_WIDTH__MASK 0xffff0000
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM1_SLICE_WIDTH__SHIFT 16
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM1_SLICE_WIDTH(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM1_SLICE_WIDTH__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM1_SLICE_WIDTH__MASK;
|
||||
}
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__MASK 0x0000ffff
|
||||
#define DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__SHIFT 0
|
||||
static inline uint32_t DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__SHIFT) & DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__MASK;
|
||||
}
|
||||
|
||||
#endif /* DSI_XML */
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#define DSI_RESET_TOGGLE_DELAY_MS 20
|
||||
|
||||
static int dsi_populate_dsc_params(struct msm_display_dsc_config *dsc);
|
||||
|
||||
static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor)
|
||||
{
|
||||
u32 ver;
|
||||
@ -157,6 +159,7 @@ struct msm_dsi_host {
|
||||
struct regmap *sfpb;
|
||||
|
||||
struct drm_display_mode *mode;
|
||||
struct msm_display_dsc_config *dsc;
|
||||
|
||||
/* connected device info */
|
||||
struct device_node *device_node;
|
||||
@ -909,6 +912,68 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable,
|
||||
dsi_write(msm_host, REG_DSI_CPHY_MODE_CTRL, BIT(0));
|
||||
}
|
||||
|
||||
static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mode, u32 hdisplay)
|
||||
{
|
||||
struct msm_display_dsc_config *dsc = msm_host->dsc;
|
||||
u32 reg, intf_width, reg_ctrl, reg_ctrl2;
|
||||
u32 slice_per_intf, total_bytes_per_intf;
|
||||
u32 pkt_per_line;
|
||||
u32 bytes_in_slice;
|
||||
u32 eol_byte_num;
|
||||
|
||||
/* first calculate dsc parameters and then program
|
||||
* compress mode registers
|
||||
*/
|
||||
intf_width = hdisplay;
|
||||
slice_per_intf = DIV_ROUND_UP(intf_width, dsc->drm->slice_width);
|
||||
|
||||
/* If slice_per_pkt is greater than slice_per_intf
|
||||
* then default to 1. This can happen during partial
|
||||
* update.
|
||||
*/
|
||||
if (slice_per_intf > dsc->drm->slice_count)
|
||||
dsc->drm->slice_count = 1;
|
||||
|
||||
slice_per_intf = DIV_ROUND_UP(hdisplay, dsc->drm->slice_width);
|
||||
bytes_in_slice = DIV_ROUND_UP(dsc->drm->slice_width * dsc->drm->bits_per_pixel, 8);
|
||||
|
||||
dsc->drm->slice_chunk_size = bytes_in_slice;
|
||||
|
||||
total_bytes_per_intf = bytes_in_slice * slice_per_intf;
|
||||
|
||||
eol_byte_num = total_bytes_per_intf % 3;
|
||||
pkt_per_line = slice_per_intf / dsc->drm->slice_count;
|
||||
|
||||
if (is_cmd_mode) /* packet data type */
|
||||
reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE);
|
||||
else
|
||||
reg = DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE(MIPI_DSI_COMPRESSED_PIXEL_STREAM);
|
||||
|
||||
/* DSI_VIDEO_COMPRESSION_MODE & DSI_COMMAND_COMPRESSION_MODE
|
||||
* registers have similar offsets, so for below common code use
|
||||
* DSI_VIDEO_COMPRESSION_MODE_XXXX for setting bits
|
||||
*/
|
||||
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE(pkt_per_line >> 1);
|
||||
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM(eol_byte_num);
|
||||
reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EN;
|
||||
|
||||
if (is_cmd_mode) {
|
||||
reg_ctrl = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL);
|
||||
reg_ctrl2 = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2);
|
||||
|
||||
reg_ctrl &= ~0xffff;
|
||||
reg_ctrl |= reg;
|
||||
|
||||
reg_ctrl2 &= ~DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__MASK;
|
||||
reg_ctrl2 |= DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH(bytes_in_slice);
|
||||
|
||||
dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
|
||||
dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
|
||||
} else {
|
||||
dsi_write(msm_host, REG_DSI_VIDEO_COMPRESSION_MODE_CTRL, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
{
|
||||
struct drm_display_mode *mode = msm_host->mode;
|
||||
@ -941,7 +1006,38 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
hdisplay /= 2;
|
||||
}
|
||||
|
||||
if (msm_host->dsc) {
|
||||
struct msm_display_dsc_config *dsc = msm_host->dsc;
|
||||
|
||||
/* update dsc params with timing params */
|
||||
if (!dsc || !mode->hdisplay || !mode->vdisplay) {
|
||||
pr_err("DSI: invalid input: pic_width: %d pic_height: %d\n",
|
||||
mode->hdisplay, mode->vdisplay);
|
||||
return;
|
||||
}
|
||||
|
||||
dsc->drm->pic_width = mode->hdisplay;
|
||||
dsc->drm->pic_height = mode->vdisplay;
|
||||
DBG("Mode %dx%d\n", dsc->drm->pic_width, dsc->drm->pic_height);
|
||||
|
||||
/* we do the calculations for dsc parameters here so that
|
||||
* panel can use these parameters
|
||||
*/
|
||||
dsi_populate_dsc_params(dsc);
|
||||
|
||||
/* Divide the display by 3 but keep back/font porch and
|
||||
* pulse width same
|
||||
*/
|
||||
h_total -= hdisplay;
|
||||
hdisplay /= 3;
|
||||
h_total += hdisplay;
|
||||
ha_end = ha_start + hdisplay;
|
||||
}
|
||||
|
||||
if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
||||
if (msm_host->dsc)
|
||||
dsi_update_dsc_timing(msm_host, false, mode->hdisplay);
|
||||
|
||||
dsi_write(msm_host, REG_DSI_ACTIVE_H,
|
||||
DSI_ACTIVE_H_START(ha_start) |
|
||||
DSI_ACTIVE_H_END(ha_end));
|
||||
@ -960,8 +1056,14 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
DSI_ACTIVE_VSYNC_VPOS_START(vs_start) |
|
||||
DSI_ACTIVE_VSYNC_VPOS_END(vs_end));
|
||||
} else { /* command mode */
|
||||
if (msm_host->dsc)
|
||||
dsi_update_dsc_timing(msm_host, true, mode->hdisplay);
|
||||
|
||||
/* image data and 1 byte write_memory_start cmd */
|
||||
wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1;
|
||||
if (!msm_host->dsc)
|
||||
wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1;
|
||||
else
|
||||
wc = mode->hdisplay / 2 + 1;
|
||||
|
||||
dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL,
|
||||
DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) |
|
||||
@ -1341,10 +1443,10 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host,
|
||||
dsi_get_bpp(msm_host->format) / 8;
|
||||
|
||||
len = dsi_cmd_dma_add(msm_host, msg);
|
||||
if (!len) {
|
||||
if (len < 0) {
|
||||
pr_err("%s: failed to add cmd type = 0x%x\n",
|
||||
__func__, msg->type);
|
||||
return -EINVAL;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* for video mode, do not send cmds more than
|
||||
@ -1363,10 +1465,14 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host,
|
||||
}
|
||||
|
||||
ret = dsi_cmd_dma_tx(msm_host, len);
|
||||
if (ret < len) {
|
||||
pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d\n",
|
||||
__func__, msg->type, (*(u8 *)(msg->tx_buf)), len);
|
||||
return -ECOMM;
|
||||
if (ret < 0) {
|
||||
pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d, ret=%d\n",
|
||||
__func__, msg->type, (*(u8 *)(msg->tx_buf)), len, ret);
|
||||
return ret;
|
||||
} else if (ret < len) {
|
||||
pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, ret=%d len=%d\n",
|
||||
__func__, msg->type, (*(u8 *)(msg->tx_buf)), ret, len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return len;
|
||||
@ -1722,6 +1828,133 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static u32 dsi_dsc_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = {
|
||||
0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62,
|
||||
0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e
|
||||
};
|
||||
|
||||
/* only 8bpc, 8bpp added */
|
||||
static char min_qp[DSC_NUM_BUF_RANGES] = {
|
||||
0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13
|
||||
};
|
||||
|
||||
static char max_qp[DSC_NUM_BUF_RANGES] = {
|
||||
4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15
|
||||
};
|
||||
|
||||
static char bpg_offset[DSC_NUM_BUF_RANGES] = {
|
||||
2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12
|
||||
};
|
||||
|
||||
static int dsi_populate_dsc_params(struct msm_display_dsc_config *dsc)
|
||||
{
|
||||
int mux_words_size;
|
||||
int groups_per_line, groups_total;
|
||||
int min_rate_buffer_size;
|
||||
int hrd_delay;
|
||||
int pre_num_extra_mux_bits, num_extra_mux_bits;
|
||||
int slice_bits;
|
||||
int target_bpp_x16;
|
||||
int data;
|
||||
int final_value, final_scale;
|
||||
int i;
|
||||
|
||||
dsc->drm->rc_model_size = 8192;
|
||||
dsc->drm->first_line_bpg_offset = 12;
|
||||
dsc->drm->rc_edge_factor = 6;
|
||||
dsc->drm->rc_tgt_offset_high = 3;
|
||||
dsc->drm->rc_tgt_offset_low = 3;
|
||||
dsc->drm->simple_422 = 0;
|
||||
dsc->drm->convert_rgb = 1;
|
||||
dsc->drm->vbr_enable = 0;
|
||||
|
||||
/* handle only bpp = bpc = 8 */
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES - 1 ; i++)
|
||||
dsc->drm->rc_buf_thresh[i] = dsi_dsc_rc_buf_thresh[i];
|
||||
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
|
||||
dsc->drm->rc_range_params[i].range_min_qp = min_qp[i];
|
||||
dsc->drm->rc_range_params[i].range_max_qp = max_qp[i];
|
||||
dsc->drm->rc_range_params[i].range_bpg_offset = bpg_offset[i];
|
||||
}
|
||||
|
||||
dsc->drm->initial_offset = 6144; /* Not bpp 12 */
|
||||
if (dsc->drm->bits_per_pixel != 8)
|
||||
dsc->drm->initial_offset = 2048; /* bpp = 12 */
|
||||
|
||||
mux_words_size = 48; /* bpc == 8/10 */
|
||||
if (dsc->drm->bits_per_component == 12)
|
||||
mux_words_size = 64;
|
||||
|
||||
dsc->drm->initial_xmit_delay = 512;
|
||||
dsc->drm->initial_scale_value = 32;
|
||||
dsc->drm->first_line_bpg_offset = 12;
|
||||
dsc->drm->line_buf_depth = dsc->drm->bits_per_component + 1;
|
||||
|
||||
/* bpc 8 */
|
||||
dsc->drm->flatness_min_qp = 3;
|
||||
dsc->drm->flatness_max_qp = 12;
|
||||
dsc->drm->rc_quant_incr_limit0 = 11;
|
||||
dsc->drm->rc_quant_incr_limit1 = 11;
|
||||
dsc->drm->mux_word_size = DSC_MUX_WORD_SIZE_8_10_BPC;
|
||||
|
||||
/* FIXME: need to call drm_dsc_compute_rc_parameters() so that rest of
|
||||
* params are calculated
|
||||
*/
|
||||
groups_per_line = DIV_ROUND_UP(dsc->drm->slice_width, 3);
|
||||
dsc->drm->slice_chunk_size = dsc->drm->slice_width * dsc->drm->bits_per_pixel / 8;
|
||||
if ((dsc->drm->slice_width * dsc->drm->bits_per_pixel) % 8)
|
||||
dsc->drm->slice_chunk_size++;
|
||||
|
||||
/* rbs-min */
|
||||
min_rate_buffer_size = dsc->drm->rc_model_size - dsc->drm->initial_offset +
|
||||
dsc->drm->initial_xmit_delay * dsc->drm->bits_per_pixel +
|
||||
groups_per_line * dsc->drm->first_line_bpg_offset;
|
||||
|
||||
hrd_delay = DIV_ROUND_UP(min_rate_buffer_size, dsc->drm->bits_per_pixel);
|
||||
|
||||
dsc->drm->initial_dec_delay = hrd_delay - dsc->drm->initial_xmit_delay;
|
||||
|
||||
dsc->drm->initial_scale_value = 8 * dsc->drm->rc_model_size /
|
||||
(dsc->drm->rc_model_size - dsc->drm->initial_offset);
|
||||
|
||||
slice_bits = 8 * dsc->drm->slice_chunk_size * dsc->drm->slice_height;
|
||||
|
||||
groups_total = groups_per_line * dsc->drm->slice_height;
|
||||
|
||||
data = dsc->drm->first_line_bpg_offset * 2048;
|
||||
|
||||
dsc->drm->nfl_bpg_offset = DIV_ROUND_UP(data, (dsc->drm->slice_height - 1));
|
||||
|
||||
pre_num_extra_mux_bits = 3 * (mux_words_size + (4 * dsc->drm->bits_per_component + 4) - 2);
|
||||
|
||||
num_extra_mux_bits = pre_num_extra_mux_bits - (mux_words_size -
|
||||
((slice_bits - pre_num_extra_mux_bits) % mux_words_size));
|
||||
|
||||
data = 2048 * (dsc->drm->rc_model_size - dsc->drm->initial_offset + num_extra_mux_bits);
|
||||
dsc->drm->slice_bpg_offset = DIV_ROUND_UP(data, groups_total);
|
||||
|
||||
/* bpp * 16 + 0.5 */
|
||||
data = dsc->drm->bits_per_pixel * 16;
|
||||
data *= 2;
|
||||
data++;
|
||||
data /= 2;
|
||||
target_bpp_x16 = data;
|
||||
|
||||
data = (dsc->drm->initial_xmit_delay * target_bpp_x16) / 16;
|
||||
final_value = dsc->drm->rc_model_size - data + num_extra_mux_bits;
|
||||
dsc->drm->final_offset = final_value;
|
||||
|
||||
final_scale = 8 * dsc->drm->rc_model_size / (dsc->drm->rc_model_size - final_value);
|
||||
|
||||
data = (final_scale - 9) * (dsc->drm->nfl_bpg_offset + dsc->drm->slice_bpg_offset);
|
||||
dsc->drm->scale_increment_interval = (2048 * dsc->drm->final_offset) / data;
|
||||
|
||||
dsc->drm->scale_decrement_interval = groups_per_line / (dsc->drm->initial_scale_value - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
|
||||
{
|
||||
struct device *dev = &msm_host->pdev->dev;
|
||||
@ -1931,9 +2164,24 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
msm_host->dev = dev;
|
||||
panel = msm_dsi_host_get_panel(&msm_host->base);
|
||||
|
||||
if (!IS_ERR(panel) && panel->dsc) {
|
||||
struct msm_display_dsc_config *dsc = msm_host->dsc;
|
||||
|
||||
if (!dsc) {
|
||||
dsc = devm_kzalloc(&msm_host->pdev->dev, sizeof(*dsc), GFP_KERNEL);
|
||||
if (!dsc)
|
||||
return -ENOMEM;
|
||||
dsc->drm = panel->dsc;
|
||||
msm_host->dsc = dsc;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K);
|
||||
if (ret) {
|
||||
pr_err("%s: alloc tx gem obj failed, %d\n", __func__, ret);
|
||||
@ -2092,9 +2340,12 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
|
||||
}
|
||||
|
||||
ret = dsi_cmds2buf_tx(msm_host, msg);
|
||||
if (ret < msg->tx_len) {
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Read cmd Tx failed, %d\n", __func__, ret);
|
||||
return ret;
|
||||
} else if (ret < msg->tx_len) {
|
||||
pr_err("%s: Read cmd Tx failed, too short: %d\n", __func__, ret);
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2409,6 +2660,32 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
struct msm_display_dsc_config *dsc = msm_host->dsc;
|
||||
int pic_width = mode->hdisplay;
|
||||
int pic_height = mode->vdisplay;
|
||||
|
||||
if (!msm_host->dsc)
|
||||
return MODE_OK;
|
||||
|
||||
if (pic_width % dsc->drm->slice_width) {
|
||||
pr_err("DSI: pic_width %d has to be multiple of slice %d\n",
|
||||
pic_width, dsc->drm->slice_width);
|
||||
return MODE_H_ILLEGAL;
|
||||
}
|
||||
|
||||
if (pic_height % dsc->drm->slice_height) {
|
||||
pr_err("DSI: pic_height %d has to be multiple of slice %d\n",
|
||||
pic_height, dsc->drm->slice_height);
|
||||
return MODE_V_ILLEGAL;
|
||||
}
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host)
|
||||
{
|
||||
return of_drm_find_panel(to_msm_dsi_host(host)->device_node);
|
||||
@ -2498,3 +2775,10 @@ void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host)
|
||||
dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER,
|
||||
DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER);
|
||||
}
|
||||
|
||||
struct msm_display_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host)
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
|
||||
return msm_host->dsc;
|
||||
}
|
||||
|
@ -573,6 +573,17 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
|
||||
dsi_mgr_bridge_power_on(bridge);
|
||||
}
|
||||
|
||||
static enum drm_mode_status dsi_mgr_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
int id = dsi_mgr_bridge_get_id(bridge);
|
||||
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
|
||||
struct mipi_dsi_host *host = msm_dsi->host;
|
||||
|
||||
return msm_dsi_host_check_dsc(host, mode);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dsi_mgr_connector_funcs = {
|
||||
.detect = dsi_mgr_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
@ -593,6 +604,7 @@ static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = {
|
||||
.disable = dsi_mgr_bridge_disable,
|
||||
.post_disable = dsi_mgr_bridge_post_disable,
|
||||
.mode_set = dsi_mgr_bridge_mode_set,
|
||||
.mode_valid = dsi_mgr_bridge_mode_valid,
|
||||
};
|
||||
|
||||
/* initialize connector when we're connected to a drm_panel */
|
||||
@ -665,6 +677,8 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id)
|
||||
bridge = &dsi_bridge->base;
|
||||
bridge->funcs = &dsi_mgr_bridge_funcs;
|
||||
|
||||
drm_bridge_add(bridge);
|
||||
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
@ -735,6 +749,7 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
|
||||
|
||||
void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge)
|
||||
{
|
||||
drm_bridge_remove(bridge);
|
||||
}
|
||||
|
||||
int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg)
|
||||
|
@ -1062,7 +1062,7 @@ const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs = {
|
||||
},
|
||||
.min_pll_rate = VCO_MIN_RATE,
|
||||
.max_pll_rate = VCO_MAX_RATE,
|
||||
.io_start = { 0xc994400, 0xc996000 },
|
||||
.io_start = { 0xc994400, 0xc996400 },
|
||||
.num_dsi_phy = 2,
|
||||
};
|
||||
|
||||
|
@ -586,7 +586,7 @@ static int dsi_7nm_set_usecase(struct msm_dsi_phy *phy)
|
||||
static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm, struct clk_hw **provided_clocks)
|
||||
{
|
||||
char clk_name[32], parent[32], vco_name[32];
|
||||
char parent2[32], parent3[32], parent4[32];
|
||||
char parent2[32];
|
||||
struct clk_init_data vco_init = {
|
||||
.parent_data = &(const struct clk_parent_data) {
|
||||
.fw_name = "ref",
|
||||
@ -687,15 +687,13 @@ static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm, struct clk_hw **provide
|
||||
snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_7nm->phy->id);
|
||||
snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->phy->id);
|
||||
snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->phy->id);
|
||||
snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_7nm->phy->id);
|
||||
snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->phy->id);
|
||||
|
||||
hw = devm_clk_hw_register_mux(dev, clk_name,
|
||||
((const char *[]){
|
||||
parent, parent2, parent3, parent4
|
||||
}), 4, 0, pll_7nm->phy->base +
|
||||
parent, parent2,
|
||||
}), 2, 0, pll_7nm->phy->base +
|
||||
REG_DSI_7nm_PHY_CMN_CLK_CFG1,
|
||||
0, 2, 0, NULL);
|
||||
0, 1, 0, NULL);
|
||||
if (IS_ERR(hw)) {
|
||||
ret = PTR_ERR(hw);
|
||||
goto fail;
|
||||
|
@ -1,78 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __EDP_CONNECTOR_H__
|
||||
#define __EDP_CONNECTOR_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/display/drm_dp_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
#define edp_read(offset) msm_readl((offset))
|
||||
#define edp_write(offset, data) msm_writel((data), (offset))
|
||||
|
||||
struct edp_ctrl;
|
||||
struct edp_aux;
|
||||
struct edp_phy;
|
||||
|
||||
struct msm_edp {
|
||||
struct drm_device *dev;
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct drm_connector *connector;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
/* the encoder we are hooked to (outside of eDP block) */
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
struct edp_ctrl *ctrl;
|
||||
|
||||
int irq;
|
||||
};
|
||||
|
||||
/* eDP bridge */
|
||||
struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp);
|
||||
void edp_bridge_destroy(struct drm_bridge *bridge);
|
||||
|
||||
/* eDP connector */
|
||||
struct drm_connector *msm_edp_connector_init(struct msm_edp *edp);
|
||||
|
||||
/* AUX */
|
||||
void *msm_edp_aux_init(struct msm_edp *edp, void __iomem *regbase, struct drm_dp_aux **drm_aux);
|
||||
void msm_edp_aux_destroy(struct device *dev, struct edp_aux *aux);
|
||||
irqreturn_t msm_edp_aux_irq(struct edp_aux *aux, u32 isr);
|
||||
void msm_edp_aux_ctrl(struct edp_aux *aux, int enable);
|
||||
|
||||
/* Phy */
|
||||
bool msm_edp_phy_ready(struct edp_phy *phy);
|
||||
void msm_edp_phy_ctrl(struct edp_phy *phy, int enable);
|
||||
void msm_edp_phy_vm_pe_init(struct edp_phy *phy);
|
||||
void msm_edp_phy_vm_pe_cfg(struct edp_phy *phy, u32 v0, u32 v1);
|
||||
void msm_edp_phy_lane_power_ctrl(struct edp_phy *phy, bool up, u32 max_lane);
|
||||
void *msm_edp_phy_init(struct device *dev, void __iomem *regbase);
|
||||
|
||||
/* Ctrl */
|
||||
irqreturn_t msm_edp_ctrl_irq(struct edp_ctrl *ctrl);
|
||||
void msm_edp_ctrl_power(struct edp_ctrl *ctrl, bool on);
|
||||
int msm_edp_ctrl_init(struct msm_edp *edp);
|
||||
void msm_edp_ctrl_destroy(struct edp_ctrl *ctrl);
|
||||
bool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl);
|
||||
int msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
|
||||
struct drm_connector *connector, struct edid **edid);
|
||||
int msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_info *info);
|
||||
/* @pixel_rate is in kHz */
|
||||
bool msm_edp_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl,
|
||||
u32 pixel_rate, u32 *pm, u32 *pn);
|
||||
|
||||
#endif /* __EDP_CONNECTOR_H__ */
|
File diff suppressed because it is too large
Load Diff
@ -142,6 +142,10 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev)
|
||||
/* HDCP needs physical address of hdmi register */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
config->mmio_name);
|
||||
if (!res) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
hdmi->mmio_phy_addr = res->start;
|
||||
|
||||
hdmi->qfprom_mmio = msm_ioremap(pdev, config->qfprom_mmio_name);
|
||||
@ -298,9 +302,9 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
|
||||
drm_connector_attach_encoder(hdmi->connector, hdmi->encoder);
|
||||
|
||||
hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (hdmi->irq < 0) {
|
||||
ret = hdmi->irq;
|
||||
DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret);
|
||||
if (!hdmi->irq) {
|
||||
ret = -EINVAL;
|
||||
DRM_DEV_ERROR(dev->dev, "failed to get irq\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -322,7 +326,6 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
|
||||
}
|
||||
|
||||
priv->bridges[priv->num_bridges++] = hdmi->bridge;
|
||||
priv->connectors[priv->num_connectors++] = hdmi->connector;
|
||||
|
||||
platform_set_drvdata(pdev, hdmi);
|
||||
|
||||
|
@ -15,6 +15,7 @@ void msm_hdmi_bridge_destroy(struct drm_bridge *bridge)
|
||||
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
|
||||
|
||||
msm_hdmi_hpd_disable(hdmi_bridge);
|
||||
drm_bridge_remove(bridge);
|
||||
}
|
||||
|
||||
static void msm_hdmi_power_on(struct drm_bridge *bridge)
|
||||
@ -349,6 +350,8 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
|
||||
DRM_BRIDGE_OP_DETECT |
|
||||
DRM_BRIDGE_OP_EDID;
|
||||
|
||||
drm_bridge_add(bridge);
|
||||
|
||||
ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
@ -232,6 +233,9 @@ static int msm_drm_uninit(struct device *dev)
|
||||
|
||||
drm_mode_config_cleanup(ddev);
|
||||
|
||||
for (i = 0; i < priv->num_bridges; i++)
|
||||
drm_bridge_remove(priv->bridges[i]);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
msm_irq_uninstall(ddev);
|
||||
pm_runtime_put_sync(dev);
|
||||
@ -256,17 +260,6 @@ static int msm_drm_uninit(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KMS_MDP4 4
|
||||
#define KMS_MDP5 5
|
||||
#define KMS_DPU 3
|
||||
|
||||
static int get_mdp_ver(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
return (int) (unsigned long) of_device_get_match_data(dev);
|
||||
}
|
||||
|
||||
#include <linux/of_address.h>
|
||||
|
||||
bool msm_use_mmu(struct drm_device *dev)
|
||||
@ -353,7 +346,6 @@ static int msm_init_vram(struct drm_device *dev)
|
||||
|
||||
static int msm_drm_init(struct device *dev, const struct drm_driver *drv)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_drm_private *priv = dev_get_drvdata(dev);
|
||||
struct drm_device *ddev;
|
||||
struct msm_kms *kms;
|
||||
@ -401,30 +393,18 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv)
|
||||
|
||||
msm_gem_shrinker_init(ddev);
|
||||
|
||||
switch (get_mdp_ver(pdev)) {
|
||||
case KMS_MDP4:
|
||||
kms = mdp4_kms_init(ddev);
|
||||
priv->kms = kms;
|
||||
break;
|
||||
case KMS_MDP5:
|
||||
kms = mdp5_kms_init(ddev);
|
||||
break;
|
||||
case KMS_DPU:
|
||||
kms = dpu_kms_init(ddev);
|
||||
priv->kms = kms;
|
||||
break;
|
||||
default:
|
||||
if (priv->kms_init) {
|
||||
ret = priv->kms_init(ddev);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to load kms\n");
|
||||
priv->kms = NULL;
|
||||
goto err_msm_uninit;
|
||||
}
|
||||
kms = priv->kms;
|
||||
} else {
|
||||
/* valid only for the dummy headless case, where of_node=NULL */
|
||||
WARN_ON(dev->of_node);
|
||||
kms = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_ERR(kms)) {
|
||||
DRM_DEV_ERROR(dev, "failed to load kms\n");
|
||||
ret = PTR_ERR(kms);
|
||||
priv->kms = NULL;
|
||||
goto err_msm_uninit;
|
||||
}
|
||||
|
||||
/* Enable normalization of plane zpos */
|
||||
@ -613,7 +593,7 @@ static int msm_ioctl_get_param(struct drm_device *dev, void *data,
|
||||
/* for now, we just have 3d pipe.. eventually this would need to
|
||||
* be more clever to dispatch to appropriate gpu module:
|
||||
*/
|
||||
if (args->pipe != MSM_PIPE_3D0)
|
||||
if ((args->pipe != MSM_PIPE_3D0) || (args->pad != 0))
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu;
|
||||
@ -622,7 +602,7 @@ static int msm_ioctl_get_param(struct drm_device *dev, void *data,
|
||||
return -ENXIO;
|
||||
|
||||
return gpu->funcs->get_param(gpu, file->driver_priv,
|
||||
args->param, &args->value);
|
||||
args->param, &args->value, &args->len);
|
||||
}
|
||||
|
||||
static int msm_ioctl_set_param(struct drm_device *dev, void *data,
|
||||
@ -632,7 +612,7 @@ static int msm_ioctl_set_param(struct drm_device *dev, void *data,
|
||||
struct drm_msm_param *args = data;
|
||||
struct msm_gpu *gpu;
|
||||
|
||||
if (args->pipe != MSM_PIPE_3D0)
|
||||
if ((args->pipe != MSM_PIPE_3D0) || (args->pad != 0))
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu;
|
||||
@ -641,7 +621,7 @@ static int msm_ioctl_set_param(struct drm_device *dev, void *data,
|
||||
return -ENXIO;
|
||||
|
||||
return gpu->funcs->set_param(gpu, file->driver_priv,
|
||||
args->param, args->value);
|
||||
args->param, args->value, args->len);
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
|
||||
@ -722,6 +702,23 @@ static int msm_ioctl_gem_info_iova(struct drm_device *dev,
|
||||
return msm_gem_get_iova(obj, ctx->aspace, iova);
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_info_set_iova(struct drm_device *dev,
|
||||
struct drm_file *file, struct drm_gem_object *obj,
|
||||
uint64_t iova)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_file_private *ctx = file->driver_priv;
|
||||
|
||||
if (!priv->gpu)
|
||||
return -EINVAL;
|
||||
|
||||
/* Only supported if per-process address space is supported: */
|
||||
if (priv->gpu->aspace == ctx->aspace)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return msm_gem_set_iova(obj, ctx->aspace, iova);
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
@ -736,6 +733,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
|
||||
switch (args->info) {
|
||||
case MSM_INFO_GET_OFFSET:
|
||||
case MSM_INFO_GET_IOVA:
|
||||
case MSM_INFO_SET_IOVA:
|
||||
/* value returned as immediate, not pointer, so len==0: */
|
||||
if (args->len)
|
||||
return -EINVAL;
|
||||
@ -760,6 +758,9 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
|
||||
case MSM_INFO_GET_IOVA:
|
||||
ret = msm_ioctl_gem_info_iova(dev, file, obj, &args->value);
|
||||
break;
|
||||
case MSM_INFO_SET_IOVA:
|
||||
ret = msm_ioctl_gem_info_set_iova(dev, file, obj, args->value);
|
||||
break;
|
||||
case MSM_INFO_SET_NAME:
|
||||
/* length check should leave room for terminating null: */
|
||||
if (args->len >= sizeof(msm_obj->name)) {
|
||||
@ -973,50 +974,7 @@ static const struct drm_driver msm_driver = {
|
||||
.patchlevel = MSM_VERSION_PATCHLEVEL,
|
||||
};
|
||||
|
||||
static int __maybe_unused msm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(dev);
|
||||
struct msm_mdss *mdss = priv->mdss;
|
||||
|
||||
DBG("");
|
||||
|
||||
if (mdss && mdss->funcs)
|
||||
return mdss->funcs->disable(mdss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(dev);
|
||||
struct msm_mdss *mdss = priv->mdss;
|
||||
|
||||
DBG("");
|
||||
|
||||
if (mdss && mdss->funcs)
|
||||
return mdss->funcs->enable(mdss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_pm_suspend(struct device *dev)
|
||||
{
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
return msm_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_pm_resume(struct device *dev)
|
||||
{
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
return msm_runtime_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_pm_prepare(struct device *dev)
|
||||
int msm_pm_prepare(struct device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(dev);
|
||||
struct drm_device *ddev = priv ? priv->dev : NULL;
|
||||
@ -1027,7 +985,7 @@ static int __maybe_unused msm_pm_prepare(struct device *dev)
|
||||
return drm_mode_config_helper_suspend(ddev);
|
||||
}
|
||||
|
||||
static void __maybe_unused msm_pm_complete(struct device *dev)
|
||||
void msm_pm_complete(struct device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev_get_drvdata(dev);
|
||||
struct drm_device *ddev = priv ? priv->dev : NULL;
|
||||
@ -1039,8 +997,6 @@ static void __maybe_unused msm_pm_complete(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops msm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(msm_pm_suspend, msm_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(msm_runtime_suspend, msm_runtime_resume, NULL)
|
||||
.prepare = msm_pm_prepare,
|
||||
.complete = msm_pm_complete,
|
||||
};
|
||||
@ -1055,25 +1011,11 @@ static const struct dev_pm_ops msm_pm_ops = {
|
||||
* is no external component that we need to add since LVDS is within MDP4
|
||||
* itself.
|
||||
*/
|
||||
static int add_components_mdp(struct device *mdp_dev,
|
||||
static int add_components_mdp(struct device *master_dev,
|
||||
struct component_match **matchptr)
|
||||
{
|
||||
struct device_node *np = mdp_dev->of_node;
|
||||
struct device_node *np = master_dev->of_node;
|
||||
struct device_node *ep_node;
|
||||
struct device *master_dev;
|
||||
|
||||
/*
|
||||
* on MDP4 based platforms, the MDP platform device is the component
|
||||
* master that adds other display interface components to itself.
|
||||
*
|
||||
* on MDP5 based platforms, the MDSS platform device is the component
|
||||
* master that adds MDP5 and other display interface components to
|
||||
* itself.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "qcom,mdp4"))
|
||||
master_dev = mdp_dev;
|
||||
else
|
||||
master_dev = mdp_dev->parent;
|
||||
|
||||
for_each_endpoint_of_node(np, ep_node) {
|
||||
struct device_node *intf;
|
||||
@ -1082,7 +1024,7 @@ static int add_components_mdp(struct device *mdp_dev,
|
||||
|
||||
ret = of_graph_parse_endpoint(ep_node, &ep);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(mdp_dev, "unable to parse port endpoint\n");
|
||||
DRM_DEV_ERROR(master_dev, "unable to parse port endpoint\n");
|
||||
of_node_put(ep_node);
|
||||
return ret;
|
||||
}
|
||||
@ -1114,60 +1056,6 @@ static int add_components_mdp(struct device *mdp_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_mdp_node(struct device *dev, void *data)
|
||||
{
|
||||
return of_match_node(dpu_dt_match, dev->of_node) ||
|
||||
of_match_node(mdp5_dt_match, dev->of_node);
|
||||
}
|
||||
|
||||
static int add_display_components(struct platform_device *pdev,
|
||||
struct component_match **matchptr)
|
||||
{
|
||||
struct device *mdp_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* MDP5/DPU based devices don't have a flat hierarchy. There is a top
|
||||
* level parent: MDSS, and children: MDP5/DPU, DSI, HDMI, eDP etc.
|
||||
* Populate the children devices, find the MDP5/DPU node, and then add
|
||||
* the interfaces to our components list.
|
||||
*/
|
||||
switch (get_mdp_ver(pdev)) {
|
||||
case KMS_MDP5:
|
||||
case KMS_DPU:
|
||||
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to populate children devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mdp_dev = device_find_child(dev, NULL, find_mdp_node);
|
||||
if (!mdp_dev) {
|
||||
DRM_DEV_ERROR(dev, "failed to find MDSS MDP node\n");
|
||||
of_platform_depopulate(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
put_device(mdp_dev);
|
||||
|
||||
/* add the MDP component itself */
|
||||
drm_of_component_match_add(dev, matchptr, component_compare_of,
|
||||
mdp_dev->of_node);
|
||||
break;
|
||||
case KMS_MDP4:
|
||||
/* MDP4 */
|
||||
mdp_dev = dev;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = add_components_mdp(mdp_dev, matchptr);
|
||||
if (ret)
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't know what's the best binding to link the gpu with the drm device.
|
||||
* Fow now, we just hunt for all the possible gpus that we support, and add them
|
||||
@ -1208,90 +1096,68 @@ static void msm_drm_unbind(struct device *dev)
|
||||
msm_drm_uninit(dev);
|
||||
}
|
||||
|
||||
static const struct component_master_ops msm_drm_ops = {
|
||||
const struct component_master_ops msm_drm_ops = {
|
||||
.bind = msm_drm_bind,
|
||||
.unbind = msm_drm_unbind,
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform driver:
|
||||
*/
|
||||
|
||||
static int msm_pdev_probe(struct platform_device *pdev)
|
||||
int msm_drv_probe(struct device *master_dev,
|
||||
int (*kms_init)(struct drm_device *dev))
|
||||
{
|
||||
struct component_match *match = NULL;
|
||||
struct msm_drm_private *priv;
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
priv = devm_kzalloc(master_dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->kms_init = kms_init;
|
||||
dev_set_drvdata(master_dev, priv);
|
||||
|
||||
switch (get_mdp_ver(pdev)) {
|
||||
case KMS_MDP5:
|
||||
ret = mdp5_mdss_init(pdev);
|
||||
break;
|
||||
case KMS_DPU:
|
||||
ret = dpu_mdss_init(pdev);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (get_mdp_ver(pdev)) {
|
||||
ret = add_display_components(pdev, &match);
|
||||
/* Add mdp components if we have KMS. */
|
||||
if (kms_init) {
|
||||
ret = add_components_mdp(master_dev, &match);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = add_gpu_components(&pdev->dev, &match);
|
||||
ret = add_gpu_components(master_dev, &match);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
/* on all devices that I am aware of, iommu's which can map
|
||||
* any address the cpu can see are used:
|
||||
*/
|
||||
ret = dma_set_mask_and_coherent(&pdev->dev, ~0);
|
||||
ret = dma_set_mask_and_coherent(master_dev, ~0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
ret = component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
|
||||
ret = component_master_add_with_match(master_dev, &msm_drm_ops, match);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fail:
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
/*
|
||||
* Platform driver:
|
||||
* Used only for headlesss GPU instances
|
||||
*/
|
||||
|
||||
if (priv->mdss && priv->mdss->funcs)
|
||||
priv->mdss->funcs->destroy(priv->mdss);
|
||||
|
||||
return ret;
|
||||
static int msm_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
return msm_drv_probe(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
static int msm_pdev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct msm_mdss *mdss = priv->mdss;
|
||||
|
||||
component_master_del(&pdev->dev, &msm_drm_ops);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
if (mdss && mdss->funcs)
|
||||
mdss->funcs->destroy(mdss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_pdev_shutdown(struct platform_device *pdev)
|
||||
void msm_drv_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_drm_private *priv = platform_get_drvdata(pdev);
|
||||
struct drm_device *drm = priv ? priv->dev : NULL;
|
||||
@ -1302,28 +1168,12 @@ static void msm_pdev_shutdown(struct platform_device *pdev)
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "qcom,mdp4", .data = (void *)KMS_MDP4 },
|
||||
{ .compatible = "qcom,mdss", .data = (void *)KMS_MDP5 },
|
||||
{ .compatible = "qcom,msm8998-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,qcm2290-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sdm845-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sc7180-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sc7280-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sc8180x-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sm8150-mdss", .data = (void *)KMS_DPU },
|
||||
{ .compatible = "qcom,sm8250-mdss", .data = (void *)KMS_DPU },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
||||
static struct platform_driver msm_platform_driver = {
|
||||
.probe = msm_pdev_probe,
|
||||
.remove = msm_pdev_remove,
|
||||
.shutdown = msm_pdev_shutdown,
|
||||
.shutdown = msm_drv_shutdown,
|
||||
.driver = {
|
||||
.name = "msm",
|
||||
.of_match_table = dt_match,
|
||||
.pm = &msm_pm_ops,
|
||||
},
|
||||
};
|
||||
@ -1340,6 +1190,8 @@ static int __init msm_drm_register(void)
|
||||
msm_hdmi_register();
|
||||
msm_dp_register();
|
||||
adreno_register();
|
||||
msm_mdp4_register();
|
||||
msm_mdss_register();
|
||||
return platform_driver_register(&msm_platform_driver);
|
||||
}
|
||||
|
||||
@ -1347,6 +1199,8 @@ static void __exit msm_drm_unregister(void)
|
||||
{
|
||||
DBG("fini");
|
||||
platform_driver_unregister(&msm_platform_driver);
|
||||
msm_mdss_unregister();
|
||||
msm_mdp4_unregister();
|
||||
msm_dp_unregister();
|
||||
msm_hdmi_unregister();
|
||||
adreno_unregister();
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/display/drm_dsc.h>
|
||||
#include <drm/msm_drm.h>
|
||||
#include <drm/drm_gem.h>
|
||||
|
||||
@ -46,20 +47,10 @@ struct msm_gem_vma;
|
||||
struct msm_disp_state;
|
||||
|
||||
#define MAX_CRTCS 8
|
||||
#define MAX_PLANES 20
|
||||
#define MAX_ENCODERS 8
|
||||
#define MAX_BRIDGES 8
|
||||
#define MAX_CONNECTORS 8
|
||||
|
||||
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
|
||||
|
||||
enum msm_mdp_plane_property {
|
||||
PLANE_PROP_ZPOS,
|
||||
PLANE_PROP_ALPHA,
|
||||
PLANE_PROP_PREMULTIPLIED,
|
||||
PLANE_PROP_MAX_NUM
|
||||
};
|
||||
|
||||
enum msm_dp_controller {
|
||||
MSM_DP_CONTROLLER_0,
|
||||
MSM_DP_CONTROLLER_1,
|
||||
@ -74,14 +65,10 @@ enum msm_dp_controller {
|
||||
* enum msm_display_caps - features/capabilities supported by displays
|
||||
* @MSM_DISPLAY_CAP_VID_MODE: Video or "active" mode supported
|
||||
* @MSM_DISPLAY_CAP_CMD_MODE: Command mode supported
|
||||
* @MSM_DISPLAY_CAP_HOT_PLUG: Hot plug detection supported
|
||||
* @MSM_DISPLAY_CAP_EDID: EDID supported
|
||||
*/
|
||||
enum msm_display_caps {
|
||||
MSM_DISPLAY_CAP_VID_MODE = BIT(0),
|
||||
MSM_DISPLAY_CAP_CMD_MODE = BIT(1),
|
||||
MSM_DISPLAY_CAP_HOT_PLUG = BIT(2),
|
||||
MSM_DISPLAY_CAP_EDID = BIT(3),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -101,12 +88,15 @@ enum msm_event_wait {
|
||||
* @num_lm: number of layer mixers used
|
||||
* @num_enc: number of compression encoder blocks used
|
||||
* @num_intf: number of interfaces the panel is mounted on
|
||||
* @num_dspp: number of dspp blocks used
|
||||
* @num_dsc: number of Display Stream Compression (DSC) blocks used
|
||||
*/
|
||||
struct msm_display_topology {
|
||||
u32 num_lm;
|
||||
u32 num_enc;
|
||||
u32 num_intf;
|
||||
u32 num_dspp;
|
||||
u32 num_dsc;
|
||||
};
|
||||
|
||||
/* Commit/Event thread specific structure */
|
||||
@ -116,18 +106,21 @@ struct msm_drm_thread {
|
||||
struct kthread_worker *worker;
|
||||
};
|
||||
|
||||
/* DSC config */
|
||||
struct msm_display_dsc_config {
|
||||
struct drm_dsc_config *drm;
|
||||
};
|
||||
|
||||
struct msm_drm_private {
|
||||
|
||||
struct drm_device *dev;
|
||||
|
||||
struct msm_kms *kms;
|
||||
int (*kms_init)(struct drm_device *dev);
|
||||
|
||||
/* subordinate devices, if present: */
|
||||
struct platform_device *gpu_pdev;
|
||||
|
||||
/* top level MDSS wrapper device (for MDP5/DPU only) */
|
||||
struct msm_mdss *mdss;
|
||||
|
||||
/* possibly this should be in the kms component, but it is
|
||||
* shared by both mdp4 and mdp5..
|
||||
*/
|
||||
@ -184,26 +177,14 @@ struct msm_drm_private {
|
||||
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
unsigned int num_planes;
|
||||
struct drm_plane *planes[MAX_PLANES];
|
||||
|
||||
unsigned int num_crtcs;
|
||||
struct drm_crtc *crtcs[MAX_CRTCS];
|
||||
|
||||
struct msm_drm_thread event_thread[MAX_CRTCS];
|
||||
|
||||
unsigned int num_encoders;
|
||||
struct drm_encoder *encoders[MAX_ENCODERS];
|
||||
|
||||
unsigned int num_bridges;
|
||||
struct drm_bridge *bridges[MAX_BRIDGES];
|
||||
|
||||
unsigned int num_connectors;
|
||||
struct drm_connector *connectors[MAX_CONNECTORS];
|
||||
|
||||
/* Properties */
|
||||
struct drm_property *plane_property[PLANE_PROP_MAX_NUM];
|
||||
|
||||
/* VRAM carveout, used when no IOMMU: */
|
||||
struct {
|
||||
unsigned long size;
|
||||
@ -250,29 +231,6 @@ void msm_atomic_state_free(struct drm_atomic_state *state);
|
||||
int msm_crtc_enable_vblank(struct drm_crtc *crtc);
|
||||
void msm_crtc_disable_vblank(struct drm_crtc *crtc);
|
||||
|
||||
int msm_gem_init_vma(struct msm_gem_address_space *aspace,
|
||||
struct msm_gem_vma *vma, int npages,
|
||||
u64 range_start, u64 range_end);
|
||||
void msm_gem_purge_vma(struct msm_gem_address_space *aspace,
|
||||
struct msm_gem_vma *vma);
|
||||
void msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
|
||||
struct msm_gem_vma *vma);
|
||||
int msm_gem_map_vma(struct msm_gem_address_space *aspace,
|
||||
struct msm_gem_vma *vma, int prot,
|
||||
struct sg_table *sgt, int npages);
|
||||
void msm_gem_close_vma(struct msm_gem_address_space *aspace,
|
||||
struct msm_gem_vma *vma);
|
||||
|
||||
|
||||
struct msm_gem_address_space *
|
||||
msm_gem_address_space_get(struct msm_gem_address_space *aspace);
|
||||
|
||||
void msm_gem_address_space_put(struct msm_gem_address_space *aspace);
|
||||
|
||||
struct msm_gem_address_space *
|
||||
msm_gem_address_space_create(struct msm_mmu *mmu, const char *name,
|
||||
u64 va_start, u64 size);
|
||||
|
||||
int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);
|
||||
void msm_unregister_mmu(struct drm_device *dev, struct msm_mmu *mmu);
|
||||
|
||||
@ -313,10 +271,20 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
|
||||
void msm_fbdev_free(struct drm_device *dev);
|
||||
|
||||
struct hdmi;
|
||||
#ifdef CONFIG_DRM_MSM_HDMI
|
||||
int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
void __init msm_hdmi_register(void);
|
||||
void __exit msm_hdmi_unregister(void);
|
||||
#else
|
||||
static inline int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void __init msm_hdmi_register(void) {}
|
||||
static inline void __exit msm_hdmi_unregister(void) {}
|
||||
#endif
|
||||
|
||||
struct msm_dsi;
|
||||
#ifdef CONFIG_DRM_MSM_DSI
|
||||
@ -330,6 +298,7 @@ void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi
|
||||
bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi);
|
||||
bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi);
|
||||
bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi);
|
||||
struct msm_display_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi);
|
||||
#else
|
||||
static inline void __init msm_dsi_register(void)
|
||||
{
|
||||
@ -358,6 +327,11 @@ static inline bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct msm_display_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_DP
|
||||
@ -365,20 +339,11 @@ int __init msm_dp_register(void);
|
||||
void __exit msm_dp_unregister(void);
|
||||
int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder);
|
||||
int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder);
|
||||
int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder);
|
||||
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode);
|
||||
|
||||
struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display,
|
||||
struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
void msm_dp_irq_postinstall(struct msm_dp *dp_display);
|
||||
void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display);
|
||||
|
||||
void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor);
|
||||
bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
|
||||
|
||||
#else
|
||||
static inline int __init msm_dp_register(void)
|
||||
@ -394,27 +359,6 @@ static inline int msm_dp_modeset_init(struct msm_dp *dp_display,
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int msm_dp_display_enable(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int msm_dp_display_disable(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int msm_dp_display_pre_disable(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void msm_dp_display_mode_set(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void msm_dp_irq_postinstall(struct msm_dp *dp_display)
|
||||
{
|
||||
@ -429,12 +373,44 @@ static inline void msm_dp_debugfs_init(struct msm_dp *dp_display,
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool msm_dp_wide_bus_available(const struct msm_dp *dp_display)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void __init msm_mdp_register(void);
|
||||
void __exit msm_mdp_unregister(void);
|
||||
void __init msm_dpu_register(void);
|
||||
void __exit msm_dpu_unregister(void);
|
||||
#ifdef CONFIG_DRM_MSM_MDP4
|
||||
void msm_mdp4_register(void);
|
||||
void msm_mdp4_unregister(void);
|
||||
#else
|
||||
static inline void msm_mdp4_register(void) {}
|
||||
static inline void msm_mdp4_unregister(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_MDP5
|
||||
void msm_mdp_register(void);
|
||||
void msm_mdp_unregister(void);
|
||||
#else
|
||||
static inline void msm_mdp_register(void) {}
|
||||
static inline void msm_mdp_unregister(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_DPU
|
||||
void msm_dpu_register(void);
|
||||
void msm_dpu_unregister(void);
|
||||
#else
|
||||
static inline void msm_dpu_register(void) {}
|
||||
static inline void msm_dpu_unregister(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_MDSS
|
||||
void msm_mdss_register(void);
|
||||
void msm_mdss_unregister(void);
|
||||
#else
|
||||
static inline void msm_mdss_register(void) {}
|
||||
static inline void msm_mdss_unregister(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
|
||||
@ -534,4 +510,16 @@ static inline unsigned long timeout_to_jiffies(const ktime_t *timeout)
|
||||
return clamp(remaining_jiffies, 0LL, (s64)INT_MAX);
|
||||
}
|
||||
|
||||
/* Driver helpers */
|
||||
|
||||
extern const struct component_master_ops msm_drm_ops;
|
||||
|
||||
int msm_pm_prepare(struct device *dev);
|
||||
void msm_pm_complete(struct device *dev);
|
||||
|
||||
int msm_drv_probe(struct device *dev,
|
||||
int (*kms_init)(struct drm_device *dev));
|
||||
void msm_drv_shutdown(struct platform_device *pdev);
|
||||
|
||||
|
||||
#endif /* __MSM_DRV_H__ */
|
||||
|
@ -21,6 +21,10 @@ struct msm_framebuffer {
|
||||
|
||||
/* Count of # of attached planes which need dirtyfb: */
|
||||
refcount_t dirtyfb;
|
||||
|
||||
/* Framebuffer per-plane address, if pinned, else zero: */
|
||||
uint64_t iova[DRM_FORMAT_MAX_PLANES];
|
||||
atomic_t prepare_count;
|
||||
};
|
||||
#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
|
||||
|
||||
@ -76,14 +80,16 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb,
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
int ret, i, n = fb->format->num_planes;
|
||||
uint64_t iova;
|
||||
|
||||
if (needs_dirtyfb)
|
||||
refcount_inc(&msm_fb->dirtyfb);
|
||||
|
||||
atomic_inc(&msm_fb->prepare_count);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
|
||||
drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
|
||||
ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &msm_fb->iova[i]);
|
||||
drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)",
|
||||
fb->base.id, i, msm_fb->iova[i], ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -103,14 +109,16 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
msm_gem_unpin_iova(fb->obj[i], aspace);
|
||||
|
||||
if (!atomic_dec_return(&msm_fb->prepare_count))
|
||||
memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
|
||||
}
|
||||
|
||||
uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
|
||||
struct msm_gem_address_space *aspace, int plane)
|
||||
{
|
||||
if (!fb->obj[plane])
|
||||
return 0;
|
||||
return msm_gem_iova(fb->obj[plane], aspace) + fb->offsets[plane];
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
return msm_fb->iova[plane];
|
||||
}
|
||||
|
||||
struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
|
||||
|
@ -15,6 +15,7 @@ msm_fence_context_alloc(struct drm_device *dev, volatile uint32_t *fenceptr,
|
||||
const char *name)
|
||||
{
|
||||
struct msm_fence_context *fctx;
|
||||
static int index = 0;
|
||||
|
||||
fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
|
||||
if (!fctx)
|
||||
@ -23,6 +24,7 @@ msm_fence_context_alloc(struct drm_device *dev, volatile uint32_t *fenceptr,
|
||||
fctx->dev = dev;
|
||||
strncpy(fctx->name, name, sizeof(fctx->name));
|
||||
fctx->context = dma_fence_context_alloc(1);
|
||||
fctx->index = index++;
|
||||
fctx->fenceptr = fenceptr;
|
||||
spin_lock_init(&fctx->spinlock);
|
||||
|
||||
@ -34,7 +36,7 @@ void msm_fence_context_free(struct msm_fence_context *fctx)
|
||||
kfree(fctx);
|
||||
}
|
||||
|
||||
static inline bool fence_completed(struct msm_fence_context *fctx, uint32_t fence)
|
||||
bool msm_fence_completed(struct msm_fence_context *fctx, uint32_t fence)
|
||||
{
|
||||
/*
|
||||
* Note: Check completed_fence first, as fenceptr is in a write-combine
|
||||
@ -76,7 +78,7 @@ static const char *msm_fence_get_timeline_name(struct dma_fence *fence)
|
||||
static bool msm_fence_signaled(struct dma_fence *fence)
|
||||
{
|
||||
struct msm_fence *f = to_msm_fence(fence);
|
||||
return fence_completed(f->fctx, f->base.seqno);
|
||||
return msm_fence_completed(f->fctx, f->base.seqno);
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops msm_fence_ops = {
|
||||
|
@ -21,6 +21,8 @@ struct msm_fence_context {
|
||||
char name[32];
|
||||
/** context: see dma_fence_context_alloc() */
|
||||
unsigned context;
|
||||
/** index: similar to context, but local to msm_fence_context's */
|
||||
unsigned index;
|
||||
|
||||
/**
|
||||
* last_fence:
|
||||
@ -56,6 +58,7 @@ struct msm_fence_context * msm_fence_context_alloc(struct drm_device *dev,
|
||||
volatile uint32_t *fenceptr, const char *name);
|
||||
void msm_fence_context_free(struct msm_fence_context *fctx);
|
||||
|
||||
bool msm_fence_completed(struct msm_fence_context *fctx, uint32_t fence);
|
||||
void msm_update_fence(struct msm_fence_context *fctx, uint32_t fence);
|
||||
|
||||
struct dma_fence * msm_fence_alloc(struct msm_fence_context *fctx);
|
||||
|
@ -376,39 +376,40 @@ put_iova_vmas(struct drm_gem_object *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static int get_iova_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t *iova,
|
||||
static struct msm_gem_vma *get_vma_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace,
|
||||
u64 range_start, u64 range_end)
|
||||
{
|
||||
struct msm_gem_vma *vma;
|
||||
int ret = 0;
|
||||
|
||||
GEM_WARN_ON(!msm_gem_is_locked(obj));
|
||||
|
||||
vma = lookup_vma(obj, aspace);
|
||||
|
||||
if (!vma) {
|
||||
int ret;
|
||||
|
||||
vma = add_vma(obj, aspace);
|
||||
if (IS_ERR(vma))
|
||||
return PTR_ERR(vma);
|
||||
return vma;
|
||||
|
||||
ret = msm_gem_init_vma(aspace, vma, obj->size >> PAGE_SHIFT,
|
||||
ret = msm_gem_init_vma(aspace, vma, obj->size,
|
||||
range_start, range_end);
|
||||
if (ret) {
|
||||
del_vma(vma);
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
} else {
|
||||
GEM_WARN_ON(vma->iova < range_start);
|
||||
GEM_WARN_ON((vma->iova + obj->size) > range_end);
|
||||
}
|
||||
|
||||
*iova = vma->iova;
|
||||
return 0;
|
||||
return vma;
|
||||
}
|
||||
|
||||
static int msm_gem_pin_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
int msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct msm_gem_vma *vma)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
struct msm_gem_vma *vma;
|
||||
struct page **pages;
|
||||
int ret, prot = IOMMU_READ;
|
||||
|
||||
@ -426,16 +427,11 @@ static int msm_gem_pin_iova(struct drm_gem_object *obj,
|
||||
if (GEM_WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED))
|
||||
return -EBUSY;
|
||||
|
||||
vma = lookup_vma(obj, aspace);
|
||||
if (GEM_WARN_ON(!vma))
|
||||
return -EINVAL;
|
||||
|
||||
pages = get_pages(obj);
|
||||
if (IS_ERR(pages))
|
||||
return PTR_ERR(pages);
|
||||
|
||||
ret = msm_gem_map_vma(aspace, vma, prot,
|
||||
msm_obj->sgt, obj->size >> PAGE_SHIFT);
|
||||
ret = msm_gem_map_vma(vma->aspace, vma, prot, msm_obj->sgt, obj->size);
|
||||
|
||||
if (!ret)
|
||||
msm_obj->pin_count++;
|
||||
@ -443,23 +439,42 @@ static int msm_gem_pin_iova(struct drm_gem_object *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void msm_gem_unpin_vma_locked(struct drm_gem_object *obj, struct msm_gem_vma *vma)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
GEM_WARN_ON(!msm_gem_is_locked(obj));
|
||||
|
||||
msm_gem_unpin_vma(vma);
|
||||
|
||||
msm_obj->pin_count--;
|
||||
GEM_WARN_ON(msm_obj->pin_count < 0);
|
||||
|
||||
update_inactive(msm_obj);
|
||||
}
|
||||
|
||||
struct msm_gem_vma *msm_gem_get_vma_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
{
|
||||
return get_vma_locked(obj, aspace, 0, U64_MAX);
|
||||
}
|
||||
|
||||
static int get_and_pin_iova_range_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t *iova,
|
||||
u64 range_start, u64 range_end)
|
||||
{
|
||||
u64 local;
|
||||
struct msm_gem_vma *vma;
|
||||
int ret;
|
||||
|
||||
GEM_WARN_ON(!msm_gem_is_locked(obj));
|
||||
|
||||
ret = get_iova_locked(obj, aspace, &local,
|
||||
range_start, range_end);
|
||||
vma = get_vma_locked(obj, aspace, range_start, range_end);
|
||||
if (IS_ERR(vma))
|
||||
return PTR_ERR(vma);
|
||||
|
||||
ret = msm_gem_pin_vma_locked(obj, vma);
|
||||
if (!ret)
|
||||
ret = msm_gem_pin_iova(obj, aspace);
|
||||
|
||||
if (!ret)
|
||||
*iova = local;
|
||||
*iova = vma->iova;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -481,12 +496,6 @@ int msm_gem_get_and_pin_iova_range(struct drm_gem_object *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msm_gem_get_and_pin_iova_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t *iova)
|
||||
{
|
||||
return get_and_pin_iova_range_locked(obj, aspace, iova, 0, U64_MAX);
|
||||
}
|
||||
|
||||
/* get iova and pin it. Should have a matching put */
|
||||
int msm_gem_get_and_pin_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t *iova)
|
||||
@ -501,52 +510,67 @@ int msm_gem_get_and_pin_iova(struct drm_gem_object *obj,
|
||||
int msm_gem_get_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t *iova)
|
||||
{
|
||||
int ret;
|
||||
struct msm_gem_vma *vma;
|
||||
int ret = 0;
|
||||
|
||||
msm_gem_lock(obj);
|
||||
ret = get_iova_locked(obj, aspace, iova, 0, U64_MAX);
|
||||
vma = get_vma_locked(obj, aspace, 0, U64_MAX);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
} else {
|
||||
*iova = vma->iova;
|
||||
}
|
||||
msm_gem_unlock(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get iova without taking a reference, used in places where you have
|
||||
* already done a 'msm_gem_get_and_pin_iova' or 'msm_gem_get_iova'
|
||||
*/
|
||||
uint64_t msm_gem_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
static int clear_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
{
|
||||
struct msm_gem_vma *vma;
|
||||
struct msm_gem_vma *vma = lookup_vma(obj, aspace);
|
||||
|
||||
msm_gem_lock(obj);
|
||||
vma = lookup_vma(obj, aspace);
|
||||
msm_gem_unlock(obj);
|
||||
GEM_WARN_ON(!vma);
|
||||
if (!vma)
|
||||
return 0;
|
||||
|
||||
return vma ? vma->iova : 0;
|
||||
if (msm_gem_vma_inuse(vma))
|
||||
return -EBUSY;
|
||||
|
||||
msm_gem_purge_vma(vma->aspace, vma);
|
||||
msm_gem_close_vma(vma->aspace, vma);
|
||||
del_vma(vma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locked variant of msm_gem_unpin_iova()
|
||||
* Get the requested iova but don't pin it. Fails if the requested iova is
|
||||
* not available. Doesn't need a put because iovas are currently valid for
|
||||
* the life of the object.
|
||||
*
|
||||
* Setting an iova of zero will clear the vma.
|
||||
*/
|
||||
void msm_gem_unpin_iova_locked(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
int msm_gem_set_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace, uint64_t iova)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
struct msm_gem_vma *vma;
|
||||
int ret = 0;
|
||||
|
||||
GEM_WARN_ON(!msm_gem_is_locked(obj));
|
||||
|
||||
vma = lookup_vma(obj, aspace);
|
||||
|
||||
if (!GEM_WARN_ON(!vma)) {
|
||||
msm_gem_unmap_vma(aspace, vma);
|
||||
|
||||
msm_obj->pin_count--;
|
||||
GEM_WARN_ON(msm_obj->pin_count < 0);
|
||||
|
||||
update_inactive(msm_obj);
|
||||
msm_gem_lock(obj);
|
||||
if (!iova) {
|
||||
ret = clear_iova(obj, aspace);
|
||||
} else {
|
||||
struct msm_gem_vma *vma;
|
||||
vma = get_vma_locked(obj, aspace, iova, iova + obj->size);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
} else if (GEM_WARN_ON(vma->iova != iova)) {
|
||||
clear_iova(obj, aspace);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
msm_gem_unlock(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -557,8 +581,13 @@ void msm_gem_unpin_iova_locked(struct drm_gem_object *obj,
|
||||
void msm_gem_unpin_iova(struct drm_gem_object *obj,
|
||||
struct msm_gem_address_space *aspace)
|
||||
{
|
||||
struct msm_gem_vma *vma;
|
||||
|
||||
msm_gem_lock(obj);
|
||||
msm_gem_unpin_iova_locked(obj, aspace);
|
||||
vma = lookup_vma(obj, aspace);
|
||||
if (!GEM_WARN_ON(!vma)) {
|
||||
msm_gem_unpin_vma_locked(obj, vma);
|
||||
}
|
||||
msm_gem_unlock(obj);
|
||||
}
|
||||
|
||||
@ -939,7 +968,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m,
|
||||
name, comm ? ":" : "", comm ? comm : "",
|
||||
vma->aspace, vma->iova,
|
||||
vma->mapped ? "mapped" : "unmapped",
|
||||
vma->inuse);
|
||||
msm_gem_vma_inuse(vma));
|
||||
kfree(comm);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user