You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'msm-next' of git://people.freedesktop.org/~robclark/linux into drm-next
Noteworthy changes this time: 1) 4k support for newer chips (ganging up hwpipes and mixers) 2) using OPP bindings for gpu 3) more prep work towards per-process pagetables * 'msm-next' of git://people.freedesktop.org/~robclark/linux: (47 commits) msm/drm: gpu: Dynamically locate the clocks from the device tree drm/msm: gpu: Use OPP tables if we can drm/msm: Hard code the GPU "slow frequency" drm/msm: Add MSM_PARAM_GMEM_BASE drm/msm: Reference count address spaces drm/msm: Make sure to detach the MMU during GPU cleanup drm/msm/mdp5: Enable 3D mux in mdp5_ctl drm/msm/mdp5: Reset CTL blend registers before configuring them drm/msm/mdp5: Assign 'right' mixer to CRTC state drm/msm/mdp5: Stage border out on base stage if CRTC has 2 LMs drm/msm/mdp5: Stage right side hwpipes on Right-side Layer Mixer drm/msm/mdp5: Prepare Layer Mixers for source split drm/msm/mdp5: Configure 'right' hwpipe drm/msm/mdp5: Assign a 'right hwpipe' to plane state drm/msm/mdp5: Create mdp5_hwpipe_mode_set drm/msm/mdp5: Add optional 'right' Layer Mixer in CRTC state drm/msm/mdp5: Add a CAP for Source Split drm/msm/mdp5: Remove mixer/intf pointers from mdp5_ctl drm/msm/mdp5: Start using parameters from CRTC state drm/msm/mdp5: Add more stuff to CRTC state ...
This commit is contained in:
@@ -40,6 +40,7 @@ msm-y := \
|
||||
mdp/mdp5/mdp5_mdss.o \
|
||||
mdp/mdp5/mdp5_kms.o \
|
||||
mdp/mdp5/mdp5_pipe.o \
|
||||
mdp/mdp5/mdp5_mixer.o \
|
||||
mdp/mdp5/mdp5_plane.o \
|
||||
mdp/mdp5/mdp5_smp.o \
|
||||
msm_atomic.o \
|
||||
|
||||
@@ -412,10 +412,8 @@ static const unsigned int a3xx_registers[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
{
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
seq_printf(m, "status: %08x\n",
|
||||
gpu_read(gpu, REG_A3XX_RBBM_STATUS));
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
adreno_show(gpu, m);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -456,12 +456,8 @@ static const unsigned int a4xx_registers[] = {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void a4xx_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
{
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
|
||||
seq_printf(m, "status: %08x\n",
|
||||
gpu_read(gpu, REG_A4XX_RBBM_STATUS));
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
|
||||
adreno_show(gpu, m);
|
||||
|
||||
}
|
||||
|
||||
@@ -638,10 +638,8 @@ static void a5xx_cp_err_irq(struct msm_gpu *gpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void a5xx_rbbm_err_irq(struct msm_gpu *gpu)
|
||||
static void a5xx_rbbm_err_irq(struct msm_gpu *gpu, u32 status)
|
||||
{
|
||||
u32 status = gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS);
|
||||
|
||||
if (status & A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR) {
|
||||
u32 val = gpu_read(gpu, REG_A5XX_RBBM_AHB_ERROR_STATUS);
|
||||
|
||||
@@ -653,6 +651,10 @@ static void a5xx_rbbm_err_irq(struct msm_gpu *gpu)
|
||||
|
||||
/* Clear the error */
|
||||
gpu_write(gpu, REG_A5XX_RBBM_AHB_CMD, (1 << 4));
|
||||
|
||||
/* Clear the interrupt */
|
||||
gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD,
|
||||
A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR);
|
||||
}
|
||||
|
||||
if (status & A5XX_RBBM_INT_0_MASK_RBBM_TRANSFER_TIMEOUT)
|
||||
@@ -704,10 +706,16 @@ static irqreturn_t a5xx_irq(struct msm_gpu *gpu)
|
||||
{
|
||||
u32 status = gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS);
|
||||
|
||||
gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD, status);
|
||||
/*
|
||||
* Clear all the interrupts except RBBM_AHB_ERROR - if we clear it
|
||||
* before the source is cleared the interrupt will storm.
|
||||
*/
|
||||
gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD,
|
||||
status & ~A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR);
|
||||
|
||||
/* Pass status to a5xx_rbbm_err_irq because we've already cleared it */
|
||||
if (status & RBBM_ERROR_MASK)
|
||||
a5xx_rbbm_err_irq(gpu);
|
||||
a5xx_rbbm_err_irq(gpu, status);
|
||||
|
||||
if (status & A5XX_RBBM_INT_0_MASK_CP_HW_ERROR)
|
||||
a5xx_cp_err_irq(gpu);
|
||||
@@ -837,12 +845,8 @@ static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value)
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
{
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
|
||||
seq_printf(m, "status: %08x\n",
|
||||
gpu_read(gpu, REG_A5XX_RBBM_STATUS));
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
|
||||
adreno_show(gpu, m);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (C) 2013-2014 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014,2017 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
@@ -17,6 +17,7 @@
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/pm_opp.h>
|
||||
#include "adreno_gpu.h"
|
||||
|
||||
#define ANY_ID 0xff
|
||||
@@ -155,21 +156,14 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
|
||||
|
||||
if (gpu) {
|
||||
int ret;
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
disable_irq(gpu->irq);
|
||||
|
||||
ret = gpu->funcs->hw_init(gpu);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
ret = msm_gpu_hw_init(gpu);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
|
||||
gpu->funcs->destroy(gpu);
|
||||
gpu = NULL;
|
||||
} else {
|
||||
enable_irq(gpu->irq);
|
||||
/* give inactive pm a chance to kick in: */
|
||||
msm_gpu_retire(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,10 +214,71 @@ static int find_chipid(struct device *dev, u32 *chipid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get legacy powerlevels from qcom,gpu-pwrlevels and populate the opp table */
|
||||
static int adreno_get_legacy_pwrlevels(struct device *dev)
|
||||
{
|
||||
struct device_node *child, *node;
|
||||
int ret;
|
||||
|
||||
node = of_find_compatible_node(dev->of_node, NULL,
|
||||
"qcom,gpu-pwrlevels");
|
||||
if (!node) {
|
||||
dev_err(dev, "Could not find the GPU powerlevels\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
unsigned int val;
|
||||
|
||||
ret = of_property_read_u32(child, "qcom,gpu-freq", &val);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Skip the intentionally bogus clock value found at the bottom
|
||||
* of most legacy frequency tables
|
||||
*/
|
||||
if (val != 27000000)
|
||||
dev_pm_opp_add(dev, val, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adreno_get_pwrlevels(struct device *dev,
|
||||
struct adreno_platform_config *config)
|
||||
{
|
||||
unsigned long freq = ULONG_MAX;
|
||||
struct dev_pm_opp *opp;
|
||||
int ret;
|
||||
|
||||
/* You down with OPP? */
|
||||
if (!of_find_property(dev->of_node, "operating-points-v2", NULL))
|
||||
ret = adreno_get_legacy_pwrlevels(dev);
|
||||
else
|
||||
ret = dev_pm_opp_of_add_table(dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Find the fastest defined rate */
|
||||
opp = dev_pm_opp_find_freq_floor(dev, &freq);
|
||||
if (!IS_ERR(opp))
|
||||
config->fast_rate = dev_pm_opp_get_freq(opp);
|
||||
|
||||
if (!config->fast_rate) {
|
||||
DRM_DEV_INFO(dev,
|
||||
"Could not find clock rate. Using default\n");
|
||||
/* Pick a suitably safe clock speed for any target */
|
||||
config->fast_rate = 200000000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adreno_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
static struct adreno_platform_config config = {};
|
||||
struct device_node *child, *node = dev->of_node;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
@@ -238,28 +293,10 @@ static int adreno_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
/* find clock rates: */
|
||||
config.fast_rate = 0;
|
||||
config.slow_rate = ~0;
|
||||
for_each_child_of_node(node, child) {
|
||||
if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) {
|
||||
struct device_node *pwrlvl;
|
||||
for_each_child_of_node(child, pwrlvl) {
|
||||
ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not find gpu-freq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
config.fast_rate = max(config.fast_rate, val);
|
||||
config.slow_rate = min(config.slow_rate, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.fast_rate) {
|
||||
dev_warn(dev, "could not find clk rates\n");
|
||||
/* This is a safe low speed for all devices: */
|
||||
config.fast_rate = 200000000;
|
||||
config.slow_rate = 27000000;
|
||||
}
|
||||
ret = adreno_get_pwrlevels(dev, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev->platform_data = &config;
|
||||
set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));
|
||||
@@ -296,12 +333,35 @@ static const struct of_device_id dt_match[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int adreno_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_gpu *gpu = platform_get_drvdata(pdev);
|
||||
|
||||
return gpu->funcs->pm_resume(gpu);
|
||||
}
|
||||
|
||||
static int adreno_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_gpu *gpu = platform_get_drvdata(pdev);
|
||||
|
||||
return gpu->funcs->pm_suspend(gpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops adreno_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(adreno_suspend, adreno_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver adreno_driver = {
|
||||
.probe = adreno_probe,
|
||||
.remove = adreno_remove,
|
||||
.driver = {
|
||||
.name = "adreno",
|
||||
.of_match_table = dt_match,
|
||||
.pm = &adreno_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
|
||||
case MSM_PARAM_GMEM_SIZE:
|
||||
*value = adreno_gpu->gmem;
|
||||
return 0;
|
||||
case MSM_PARAM_GMEM_BASE:
|
||||
*value = 0x100000;
|
||||
return 0;
|
||||
case MSM_PARAM_CHIP_ID:
|
||||
*value = adreno_gpu->rev.patchid |
|
||||
(adreno_gpu->rev.minor << 8) |
|
||||
@@ -68,6 +71,14 @@ int adreno_hw_init(struct msm_gpu *gpu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* reset ringbuffer: */
|
||||
gpu->rb->cur = gpu->rb->start;
|
||||
|
||||
/* reset completed fence seqno: */
|
||||
adreno_gpu->memptrs->fence = gpu->fctx->completed_fence;
|
||||
adreno_gpu->memptrs->rptr = 0;
|
||||
adreno_gpu->memptrs->wptr = 0;
|
||||
|
||||
/* Setup REG_CP_RB_CNTL: */
|
||||
adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_CNTL,
|
||||
/* size is log2(quad-words): */
|
||||
@@ -111,29 +122,20 @@ uint32_t adreno_last_fence(struct msm_gpu *gpu)
|
||||
|
||||
void adreno_recover(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
struct drm_device *dev = gpu->dev;
|
||||
int ret;
|
||||
|
||||
// XXX pm-runtime?? we *need* the device to be off after this
|
||||
// so maybe continuing to call ->pm_suspend/resume() is better?
|
||||
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
|
||||
/* reset ringbuffer: */
|
||||
gpu->rb->cur = gpu->rb->start;
|
||||
|
||||
/* reset completed fence seqno: */
|
||||
adreno_gpu->memptrs->fence = gpu->fctx->completed_fence;
|
||||
adreno_gpu->memptrs->rptr = 0;
|
||||
adreno_gpu->memptrs->wptr = 0;
|
||||
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
|
||||
disable_irq(gpu->irq);
|
||||
ret = gpu->funcs->hw_init(gpu);
|
||||
ret = msm_gpu_hw_init(gpu);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
|
||||
/* hmm, oh well? */
|
||||
}
|
||||
enable_irq(gpu->irq);
|
||||
}
|
||||
|
||||
void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
@@ -259,8 +261,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr);
|
||||
seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
|
||||
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
|
||||
/* dump these out in a form that can be parsed by demsm: */
|
||||
seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
|
||||
for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
|
||||
@@ -273,8 +273,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
|
||||
}
|
||||
}
|
||||
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -354,14 +352,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
adreno_gpu->rev = config->rev;
|
||||
|
||||
gpu->fast_rate = config->fast_rate;
|
||||
gpu->slow_rate = config->slow_rate;
|
||||
gpu->bus_freq = config->bus_freq;
|
||||
#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
|
||||
gpu->bus_scale_table = config->bus_scale_table;
|
||||
#endif
|
||||
|
||||
DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
|
||||
gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
|
||||
DBG("fast_rate=%u, slow_rate=27000000, bus_freq=%u",
|
||||
gpu->fast_rate, gpu->bus_freq);
|
||||
|
||||
ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base,
|
||||
adreno_gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq",
|
||||
@@ -369,6 +366,10 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, DRM_MSM_INACTIVE_PERIOD);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = request_firmware(&adreno_gpu->pm4, adreno_gpu->info->pm4fw, drm->dev);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n",
|
||||
@@ -439,6 +440,6 @@ void adreno_gpu_cleanup(struct adreno_gpu *adreno_gpu)
|
||||
if (gpu->aspace) {
|
||||
gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu,
|
||||
iommu_ports, ARRAY_SIZE(iommu_ports));
|
||||
msm_gem_address_space_destroy(gpu->aspace);
|
||||
msm_gem_address_space_put(gpu->aspace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ struct adreno_gpu {
|
||||
/* platform config data (ie. from DT, or pdata) */
|
||||
struct adreno_platform_config {
|
||||
struct adreno_rev rev;
|
||||
uint32_t fast_rate, slow_rate, bus_freq;
|
||||
uint32_t fast_rate, bus_freq;
|
||||
#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
|
||||
struct msm_bus_scale_pdata *bus_scale_table;
|
||||
#endif
|
||||
|
||||
@@ -114,15 +114,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = mdp4_crtc->event;
|
||||
if (event) {
|
||||
/* if regular vblank case (!file) or if cancel-flip from
|
||||
* preclose on file that requested flip, then send the
|
||||
* event:
|
||||
*/
|
||||
if (!file || (event->base.file_priv == file)) {
|
||||
mdp4_crtc->event = NULL;
|
||||
DBG("%s: send event: %p", mdp4_crtc->name, event);
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
mdp4_crtc->event = NULL;
|
||||
DBG("%s: send event: %p", mdp4_crtc->name, event);
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ static void mdp4_destroy(struct msm_kms *kms)
|
||||
if (aspace) {
|
||||
aspace->mmu->funcs->detach(aspace->mmu,
|
||||
iommu_ports, ARRAY_SIZE(iommu_ports));
|
||||
msm_gem_address_space_destroy(aspace);
|
||||
msm_gem_address_space_put(aspace);
|
||||
}
|
||||
|
||||
if (mdp4_kms->rpm_enabled)
|
||||
|
||||
@@ -70,6 +70,18 @@ const struct mdp5_cfg_hw msm8x74v1_config = {
|
||||
.lm = {
|
||||
.count = 5,
|
||||
.base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 1, .pp = 1, .dspp = 1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 2, .pp = 2, .dspp = 2,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB },
|
||||
{ .id = 4, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB },
|
||||
},
|
||||
.nb_stages = 5,
|
||||
},
|
||||
.dspp = {
|
||||
@@ -134,6 +146,18 @@ const struct mdp5_cfg_hw msm8x74v2_config = {
|
||||
.lm = {
|
||||
.count = 5,
|
||||
.base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 1, .pp = 1, .dspp = 1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 2, .pp = 2, .dspp = 2,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 4, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
},
|
||||
.nb_stages = 5,
|
||||
.max_width = 2048,
|
||||
.max_height = 0xFFFF,
|
||||
@@ -167,6 +191,7 @@ const struct mdp5_cfg_hw apq8084_config = {
|
||||
.mdp = {
|
||||
.count = 1,
|
||||
.caps = MDP_CAP_SMP |
|
||||
MDP_CAP_SRC_SPLIT |
|
||||
0,
|
||||
},
|
||||
.smp = {
|
||||
@@ -211,6 +236,22 @@ const struct mdp5_cfg_hw apq8084_config = {
|
||||
.lm = {
|
||||
.count = 6,
|
||||
.base = { 0x03900, 0x03d00, 0x04100, 0x04500, 0x04900, 0x04d00 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 1, .pp = 1, .dspp = 1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 2, .pp = 2, .dspp = 2,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 4, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 5, .pp = 3, .dspp = 3,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
},
|
||||
.nb_stages = 5,
|
||||
.max_width = 2048,
|
||||
.max_height = 0xFFFF,
|
||||
@@ -282,6 +323,12 @@ const struct mdp5_cfg_hw msm8x16_config = {
|
||||
.lm = {
|
||||
.count = 2, /* LM0 and LM3 */
|
||||
.base = { 0x44000, 0x47000 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB },
|
||||
},
|
||||
.nb_stages = 8,
|
||||
.max_width = 2048,
|
||||
.max_height = 0xFFFF,
|
||||
@@ -306,6 +353,7 @@ const struct mdp5_cfg_hw msm8x94_config = {
|
||||
.mdp = {
|
||||
.count = 1,
|
||||
.caps = MDP_CAP_SMP |
|
||||
MDP_CAP_SRC_SPLIT |
|
||||
0,
|
||||
},
|
||||
.smp = {
|
||||
@@ -350,6 +398,22 @@ const struct mdp5_cfg_hw msm8x94_config = {
|
||||
.lm = {
|
||||
.count = 6,
|
||||
.base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 1, .pp = 1, .dspp = 1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 2, .pp = 2, .dspp = 2,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 4, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 5, .pp = 3, .dspp = 3,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
},
|
||||
.nb_stages = 8,
|
||||
.max_width = 2048,
|
||||
.max_height = 0xFFFF,
|
||||
@@ -385,6 +449,7 @@ const struct mdp5_cfg_hw msm8x96_config = {
|
||||
.count = 1,
|
||||
.caps = MDP_CAP_DSC |
|
||||
MDP_CAP_CDM |
|
||||
MDP_CAP_SRC_SPLIT |
|
||||
0,
|
||||
},
|
||||
.ctl = {
|
||||
@@ -434,6 +499,22 @@ const struct mdp5_cfg_hw msm8x96_config = {
|
||||
.lm = {
|
||||
.count = 6,
|
||||
.base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 1, .pp = 1, .dspp = 1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
{ .id = 2, .pp = 2, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR, },
|
||||
{ .id = 3, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 4, .pp = -1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_WB, },
|
||||
{ .id = 5, .pp = 3, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_DISPLAY, },
|
||||
},
|
||||
.nb_stages = 8,
|
||||
.max_width = 2560,
|
||||
.max_height = 0xFFFF,
|
||||
|
||||
@@ -39,8 +39,16 @@ struct mdp5_sub_block {
|
||||
MDP5_SUB_BLOCK_DEFINITION;
|
||||
};
|
||||
|
||||
struct mdp5_lm_instance {
|
||||
int id;
|
||||
int pp;
|
||||
int dspp;
|
||||
uint32_t caps;
|
||||
};
|
||||
|
||||
struct mdp5_lm_block {
|
||||
MDP5_SUB_BLOCK_DEFINITION;
|
||||
struct mdp5_lm_instance instances[MAX_BASES];
|
||||
uint32_t nb_stages; /* number of stages per blender */
|
||||
uint32_t max_width; /* Maximum output resolution */
|
||||
uint32_t max_height;
|
||||
|
||||
@@ -51,7 +51,8 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
|
||||
struct device *dev = encoder->dev->dev;
|
||||
u32 total_lines_x100, vclks_line, cfg;
|
||||
long vsync_clk_speed;
|
||||
int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
|
||||
struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
|
||||
int pp_id = mixer->pp;
|
||||
|
||||
if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
|
||||
dev_err(dev, "vsync_clk is not initialized\n");
|
||||
@@ -94,7 +95,8 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
|
||||
static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
|
||||
struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
|
||||
int pp_id = mixer->pp;
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(mdp5_kms->vsync_clk,
|
||||
@@ -119,7 +121,8 @@ static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
|
||||
static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
|
||||
struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
|
||||
int pp_id = mixer->pp;
|
||||
|
||||
mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
|
||||
clk_disable_unprepare(mdp5_kms->vsync_clk);
|
||||
@@ -129,8 +132,6 @@ void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
|
||||
|
||||
mode = adjusted_mode;
|
||||
|
||||
DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
|
||||
@@ -142,23 +143,23 @@ void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
mode->type, mode->flags);
|
||||
pingpong_tearcheck_setup(encoder, mode);
|
||||
mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_cmd_enc->intf,
|
||||
mdp5_cmd_enc->ctl);
|
||||
mdp5_crtc_set_pipeline(encoder->crtc);
|
||||
}
|
||||
|
||||
void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
|
||||
struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
|
||||
struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
|
||||
struct mdp5_interface *intf = mdp5_cmd_enc->intf;
|
||||
struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
|
||||
|
||||
if (WARN_ON(!mdp5_cmd_enc->enabled))
|
||||
return;
|
||||
|
||||
pingpong_tearcheck_disable(encoder);
|
||||
|
||||
mdp5_ctl_set_encoder_state(ctl, false);
|
||||
mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
|
||||
mdp5_ctl_set_encoder_state(ctl, pipeline, false);
|
||||
mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf));
|
||||
|
||||
bs_set(mdp5_cmd_enc, 0);
|
||||
|
||||
@@ -169,7 +170,8 @@ void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
|
||||
struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
|
||||
struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
|
||||
struct mdp5_interface *intf = mdp5_cmd_enc->intf;
|
||||
struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
|
||||
|
||||
if (WARN_ON(mdp5_cmd_enc->enabled))
|
||||
return;
|
||||
@@ -178,9 +180,9 @@ void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
|
||||
if (pingpong_tearcheck_enable(encoder))
|
||||
return;
|
||||
|
||||
mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
|
||||
mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf));
|
||||
|
||||
mdp5_ctl_set_encoder_state(ctl, true);
|
||||
mdp5_ctl_set_encoder_state(ctl, pipeline, true);
|
||||
|
||||
mdp5_cmd_enc->enabled = true;
|
||||
}
|
||||
@@ -197,7 +199,7 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
|
||||
return -EINVAL;
|
||||
|
||||
mdp5_kms = get_kms(encoder);
|
||||
intf_num = mdp5_cmd_enc->intf.num;
|
||||
intf_num = mdp5_cmd_enc->intf->num;
|
||||
|
||||
/* Switch slave encoder's trigger MUX, to use the master's
|
||||
* start signal for the slave encoder
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,24 +32,16 @@
|
||||
#define CTL_STAT_BUSY 0x1
|
||||
#define CTL_STAT_BOOKED 0x2
|
||||
|
||||
struct op_mode {
|
||||
struct mdp5_interface intf;
|
||||
|
||||
bool encoder_enabled;
|
||||
uint32_t start_mask;
|
||||
};
|
||||
|
||||
struct mdp5_ctl {
|
||||
struct mdp5_ctl_manager *ctlm;
|
||||
|
||||
u32 id;
|
||||
int lm;
|
||||
|
||||
/* CTL status bitmask */
|
||||
u32 status;
|
||||
|
||||
/* Operation Mode Configuration for the Pipeline */
|
||||
struct op_mode pipeline;
|
||||
bool encoder_enabled;
|
||||
uint32_t start_mask;
|
||||
|
||||
/* REG_MDP5_CTL_*(<id>) registers access info + lock: */
|
||||
spinlock_t hw_lock;
|
||||
@@ -146,9 +138,10 @@ static void set_display_intf(struct mdp5_kms *mdp5_kms,
|
||||
spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags);
|
||||
}
|
||||
|
||||
static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf)
|
||||
static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mdp5_interface *intf = pipeline->intf;
|
||||
u32 ctl_op = 0;
|
||||
|
||||
if (!mdp5_cfg_intf_is_virtual(intf->type))
|
||||
@@ -169,52 +162,50 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pipeline->r_mixer)
|
||||
ctl_op |= MDP5_CTL_OP_PACK_3D_ENABLE |
|
||||
MDP5_CTL_OP_PACK_3D(1);
|
||||
|
||||
spin_lock_irqsave(&ctl->hw_lock, flags);
|
||||
ctl_write(ctl, REG_MDP5_CTL_OP(ctl->id), ctl_op);
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
}
|
||||
|
||||
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl,
|
||||
struct mdp5_interface *intf, int lm)
|
||||
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline)
|
||||
{
|
||||
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
|
||||
struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr);
|
||||
struct mdp5_interface *intf = pipeline->intf;
|
||||
struct mdp5_hw_mixer *mixer = pipeline->mixer;
|
||||
struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
|
||||
|
||||
if (unlikely(WARN_ON(intf->num != ctl->pipeline.intf.num))) {
|
||||
dev_err(mdp5_kms->dev->dev,
|
||||
"CTL %d is allocated by INTF %d, but used by INTF %d\n",
|
||||
ctl->id, ctl->pipeline.intf.num, intf->num);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctl->lm = lm;
|
||||
|
||||
memcpy(&ctl->pipeline.intf, intf, sizeof(*intf));
|
||||
|
||||
ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(ctl->lm) |
|
||||
mdp_ctl_flush_mask_encoder(intf);
|
||||
ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm) |
|
||||
mdp_ctl_flush_mask_encoder(intf);
|
||||
if (r_mixer)
|
||||
ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm);
|
||||
|
||||
/* Virtual interfaces need not set a display intf (e.g.: Writeback) */
|
||||
if (!mdp5_cfg_intf_is_virtual(intf->type))
|
||||
set_display_intf(mdp5_kms, intf);
|
||||
|
||||
set_ctl_op(ctl, intf);
|
||||
set_ctl_op(ctl, pipeline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool start_signal_needed(struct mdp5_ctl *ctl)
|
||||
static bool start_signal_needed(struct mdp5_ctl *ctl,
|
||||
struct mdp5_pipeline *pipeline)
|
||||
{
|
||||
struct op_mode *pipeline = &ctl->pipeline;
|
||||
struct mdp5_interface *intf = pipeline->intf;
|
||||
|
||||
if (!pipeline->encoder_enabled || pipeline->start_mask != 0)
|
||||
if (!ctl->encoder_enabled || ctl->start_mask != 0)
|
||||
return false;
|
||||
|
||||
switch (pipeline->intf.type) {
|
||||
switch (intf->type) {
|
||||
case INTF_WB:
|
||||
return true;
|
||||
case INTF_DSI:
|
||||
return pipeline->intf.mode == MDP5_INTF_DSI_MODE_COMMAND;
|
||||
return intf->mode == MDP5_INTF_DSI_MODE_COMMAND;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -236,19 +227,23 @@ static void send_start_signal(struct mdp5_ctl *ctl)
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
}
|
||||
|
||||
static void refill_start_mask(struct mdp5_ctl *ctl)
|
||||
static void refill_start_mask(struct mdp5_ctl *ctl,
|
||||
struct mdp5_pipeline *pipeline)
|
||||
{
|
||||
struct op_mode *pipeline = &ctl->pipeline;
|
||||
struct mdp5_interface *intf = &ctl->pipeline.intf;
|
||||
struct mdp5_interface *intf = pipeline->intf;
|
||||
struct mdp5_hw_mixer *mixer = pipeline->mixer;
|
||||
struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
|
||||
|
||||
pipeline->start_mask = mdp_ctl_flush_mask_lm(ctl->lm);
|
||||
ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm);
|
||||
if (r_mixer)
|
||||
ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm);
|
||||
|
||||
/*
|
||||
* Writeback encoder needs to program & flush
|
||||
* address registers for each page flip..
|
||||
*/
|
||||
if (intf->type == INTF_WB)
|
||||
pipeline->start_mask |= mdp_ctl_flush_mask_encoder(intf);
|
||||
ctl->start_mask |= mdp_ctl_flush_mask_encoder(intf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,17 +254,21 @@ static void refill_start_mask(struct mdp5_ctl *ctl)
|
||||
* Note:
|
||||
* This encoder state is needed to trigger START signal (data path kickoff).
|
||||
*/
|
||||
int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled)
|
||||
int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl,
|
||||
struct mdp5_pipeline *pipeline,
|
||||
bool enabled)
|
||||
{
|
||||
struct mdp5_interface *intf = pipeline->intf;
|
||||
|
||||
if (WARN_ON(!ctl))
|
||||
return -EINVAL;
|
||||
|
||||
ctl->pipeline.encoder_enabled = enabled;
|
||||
DBG("intf_%d: %s", ctl->pipeline.intf.num, enabled ? "on" : "off");
|
||||
ctl->encoder_enabled = enabled;
|
||||
DBG("intf_%d: %s", intf->num, enabled ? "on" : "off");
|
||||
|
||||
if (start_signal_needed(ctl)) {
|
||||
if (start_signal_needed(ctl, pipeline)) {
|
||||
send_start_signal(ctl);
|
||||
refill_start_mask(ctl);
|
||||
refill_start_mask(ctl, pipeline);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -280,29 +279,35 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled)
|
||||
* CTL registers need to be flushed after calling this function
|
||||
* (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask)
|
||||
*/
|
||||
int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable)
|
||||
int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
int cursor_id, bool enable)
|
||||
{
|
||||
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
|
||||
unsigned long flags;
|
||||
u32 blend_cfg;
|
||||
int lm = ctl->lm;
|
||||
struct mdp5_hw_mixer *mixer = pipeline->mixer;
|
||||
|
||||
if (unlikely(WARN_ON(lm < 0))) {
|
||||
dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM: %d",
|
||||
ctl->id, lm);
|
||||
if (unlikely(WARN_ON(!mixer))) {
|
||||
dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM",
|
||||
ctl->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pipeline->r_mixer) {
|
||||
dev_err(ctl_mgr->dev->dev, "unsupported configuration");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ctl->hw_lock, flags);
|
||||
|
||||
blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm));
|
||||
blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm));
|
||||
|
||||
if (enable)
|
||||
blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT;
|
||||
else
|
||||
blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT;
|
||||
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg);
|
||||
ctl->cursor_on = enable;
|
||||
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
@@ -355,37 +360,88 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe,
|
||||
}
|
||||
}
|
||||
|
||||
int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt,
|
||||
u32 ctl_blend_op_flags)
|
||||
static void mdp5_ctl_reset_blend_regs(struct mdp5_ctl *ctl)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&ctl->hw_lock, flags);
|
||||
|
||||
for (i = 0; i < ctl_mgr->nlm; i++) {
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, i), 0x0);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, i), 0x0);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
}
|
||||
|
||||
#define PIPE_LEFT 0
|
||||
#define PIPE_RIGHT 1
|
||||
int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
enum mdp5_pipe stage[][MAX_PIPE_STAGE],
|
||||
enum mdp5_pipe r_stage[][MAX_PIPE_STAGE],
|
||||
u32 stage_cnt, u32 ctl_blend_op_flags)
|
||||
{
|
||||
struct mdp5_hw_mixer *mixer = pipeline->mixer;
|
||||
struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
|
||||
unsigned long flags;
|
||||
u32 blend_cfg = 0, blend_ext_cfg = 0;
|
||||
u32 r_blend_cfg = 0, r_blend_ext_cfg = 0;
|
||||
int i, start_stage;
|
||||
|
||||
mdp5_ctl_reset_blend_regs(ctl);
|
||||
|
||||
if (ctl_blend_op_flags & MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT) {
|
||||
start_stage = STAGE0;
|
||||
blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR;
|
||||
if (r_mixer)
|
||||
r_blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR;
|
||||
} else {
|
||||
start_stage = STAGE_BASE;
|
||||
}
|
||||
|
||||
for (i = start_stage; stage_cnt && i <= STAGE_MAX; i++) {
|
||||
blend_cfg |= mdp_ctl_blend_mask(stage[i], i);
|
||||
blend_ext_cfg |= mdp_ctl_blend_ext_mask(stage[i], i);
|
||||
blend_cfg |=
|
||||
mdp_ctl_blend_mask(stage[i][PIPE_LEFT], i) |
|
||||
mdp_ctl_blend_mask(stage[i][PIPE_RIGHT], i);
|
||||
blend_ext_cfg |=
|
||||
mdp_ctl_blend_ext_mask(stage[i][PIPE_LEFT], i) |
|
||||
mdp_ctl_blend_ext_mask(stage[i][PIPE_RIGHT], i);
|
||||
if (r_mixer) {
|
||||
r_blend_cfg |=
|
||||
mdp_ctl_blend_mask(r_stage[i][PIPE_LEFT], i) |
|
||||
mdp_ctl_blend_mask(r_stage[i][PIPE_RIGHT], i);
|
||||
r_blend_ext_cfg |=
|
||||
mdp_ctl_blend_ext_mask(r_stage[i][PIPE_LEFT], i) |
|
||||
mdp_ctl_blend_ext_mask(r_stage[i][PIPE_RIGHT], i);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ctl->hw_lock, flags);
|
||||
if (ctl->cursor_on)
|
||||
blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT;
|
||||
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, ctl->lm), blend_cfg);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, ctl->lm), blend_ext_cfg);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, mixer->lm),
|
||||
blend_ext_cfg);
|
||||
if (r_mixer) {
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, r_mixer->lm),
|
||||
r_blend_cfg);
|
||||
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, r_mixer->lm),
|
||||
r_blend_ext_cfg);
|
||||
}
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
|
||||
ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(ctl->lm);
|
||||
ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(mixer->lm);
|
||||
if (r_mixer)
|
||||
ctl->pending_ctl_trigger |= mdp_ctl_flush_mask_lm(r_mixer->lm);
|
||||
|
||||
DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", ctl->lm,
|
||||
DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", mixer->lm,
|
||||
blend_cfg, blend_ext_cfg);
|
||||
if (r_mixer)
|
||||
DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x",
|
||||
r_mixer->lm, r_blend_cfg, r_blend_ext_cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -443,7 +499,8 @@ u32 mdp_ctl_flush_mask_lm(int lm)
|
||||
}
|
||||
}
|
||||
|
||||
static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask)
|
||||
static u32 fix_sw_flush(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
u32 flush_mask)
|
||||
{
|
||||
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
|
||||
u32 sw_mask = 0;
|
||||
@@ -452,7 +509,7 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask)
|
||||
|
||||
/* for some targets, cursor bit is the same as LM bit */
|
||||
if (BIT_NEEDS_SW_FIX(MDP5_CTL_FLUSH_CURSOR_0))
|
||||
sw_mask |= mdp_ctl_flush_mask_lm(ctl->lm);
|
||||
sw_mask |= mdp_ctl_flush_mask_lm(pipeline->mixer->lm);
|
||||
|
||||
return sw_mask;
|
||||
}
|
||||
@@ -498,25 +555,26 @@ static void fix_for_single_flush(struct mdp5_ctl *ctl, u32 *flush_mask,
|
||||
*
|
||||
* Return H/W flushed bit mask.
|
||||
*/
|
||||
u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask)
|
||||
u32 mdp5_ctl_commit(struct mdp5_ctl *ctl,
|
||||
struct mdp5_pipeline *pipeline,
|
||||
u32 flush_mask)
|
||||
{
|
||||
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
|
||||
struct op_mode *pipeline = &ctl->pipeline;
|
||||
unsigned long flags;
|
||||
u32 flush_id = ctl->id;
|
||||
u32 curr_ctl_flush_mask;
|
||||
|
||||
pipeline->start_mask &= ~flush_mask;
|
||||
ctl->start_mask &= ~flush_mask;
|
||||
|
||||
VERB("flush_mask=%x, start_mask=%x, trigger=%x", flush_mask,
|
||||
pipeline->start_mask, ctl->pending_ctl_trigger);
|
||||
ctl->start_mask, ctl->pending_ctl_trigger);
|
||||
|
||||
if (ctl->pending_ctl_trigger & flush_mask) {
|
||||
flush_mask |= MDP5_CTL_FLUSH_CTL;
|
||||
ctl->pending_ctl_trigger = 0;
|
||||
}
|
||||
|
||||
flush_mask |= fix_sw_flush(ctl, flush_mask);
|
||||
flush_mask |= fix_sw_flush(ctl, pipeline, flush_mask);
|
||||
|
||||
flush_mask &= ctl_mgr->flush_hw_mask;
|
||||
|
||||
@@ -530,9 +588,9 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask)
|
||||
spin_unlock_irqrestore(&ctl->hw_lock, flags);
|
||||
}
|
||||
|
||||
if (start_signal_needed(ctl)) {
|
||||
if (start_signal_needed(ctl, pipeline)) {
|
||||
send_start_signal(ctl);
|
||||
refill_start_mask(ctl);
|
||||
refill_start_mask(ctl, pipeline);
|
||||
}
|
||||
|
||||
return curr_ctl_flush_mask;
|
||||
@@ -619,8 +677,6 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr,
|
||||
|
||||
found:
|
||||
ctl = &ctl_mgr->ctls[c];
|
||||
ctl->pipeline.intf.num = intf_num;
|
||||
ctl->lm = -1;
|
||||
ctl->status |= CTL_STAT_BUSY;
|
||||
ctl->pending_ctl_trigger = 0;
|
||||
DBG("CTL %d allocated", ctl->id);
|
||||
|
||||
@@ -37,13 +37,17 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, int intf_num);
|
||||
int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl);
|
||||
|
||||
struct mdp5_interface;
|
||||
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf,
|
||||
int lm);
|
||||
int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled);
|
||||
struct mdp5_pipeline;
|
||||
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *p);
|
||||
int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, struct mdp5_pipeline *p,
|
||||
bool enabled);
|
||||
|
||||
int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable);
|
||||
int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
int cursor_id, bool enable);
|
||||
int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable);
|
||||
|
||||
#define MAX_PIPE_STAGE 2
|
||||
|
||||
/*
|
||||
* mdp5_ctl_blend() - Blend multiple layers on a Layer Mixer (LM)
|
||||
*
|
||||
@@ -56,8 +60,10 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable);
|
||||
* (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask)
|
||||
*/
|
||||
#define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0)
|
||||
int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt,
|
||||
u32 ctl_blend_op_flags);
|
||||
int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
enum mdp5_pipe stage[][MAX_PIPE_STAGE],
|
||||
enum mdp5_pipe r_stage[][MAX_PIPE_STAGE],
|
||||
u32 stage_cnt, u32 ctl_blend_op_flags);
|
||||
|
||||
/**
|
||||
* mdp_ctl_flush_mask...() - Register FLUSH masks
|
||||
@@ -71,7 +77,8 @@ u32 mdp_ctl_flush_mask_cursor(int cursor_id);
|
||||
u32 mdp_ctl_flush_mask_encoder(struct mdp5_interface *intf);
|
||||
|
||||
/* @flush_mask: see CTL flush masks definitions below */
|
||||
u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask);
|
||||
u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
|
||||
u32 flush_mask);
|
||||
u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl);
|
||||
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_connector *connector;
|
||||
int intf = mdp5_encoder->intf.num;
|
||||
int intf = mdp5_encoder->intf->num;
|
||||
uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol;
|
||||
uint32_t display_v_start, display_v_end;
|
||||
uint32_t hsync_start_x, hsync_end_x;
|
||||
@@ -130,7 +130,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder,
|
||||
ctrl_pol = 0;
|
||||
|
||||
/* DSI controller cannot handle active-low sync signals. */
|
||||
if (mdp5_encoder->intf.type != INTF_DSI) {
|
||||
if (mdp5_encoder->intf->type != INTF_DSI) {
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW;
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
@@ -175,7 +175,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder,
|
||||
* DISPLAY_V_START = (VBP * HCYCLE) + HBP
|
||||
* DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP
|
||||
*/
|
||||
if (mdp5_encoder->intf.type == INTF_eDP) {
|
||||
if (mdp5_encoder->intf->type == INTF_eDP) {
|
||||
display_v_start += mode->htotal - mode->hsync_start;
|
||||
display_v_end -= mode->hsync_start - mode->hdisplay;
|
||||
}
|
||||
@@ -206,8 +206,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder,
|
||||
|
||||
spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
|
||||
|
||||
mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_encoder->intf,
|
||||
mdp5_encoder->ctl);
|
||||
mdp5_crtc_set_pipeline(encoder->crtc);
|
||||
}
|
||||
|
||||
static void mdp5_vid_encoder_disable(struct drm_encoder *encoder)
|
||||
@@ -215,20 +214,21 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder)
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
struct mdp5_ctl *ctl = mdp5_encoder->ctl;
|
||||
int lm = mdp5_crtc_get_lm(encoder->crtc);
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
int intfn = mdp5_encoder->intf.num;
|
||||
struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
|
||||
struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
int intfn = mdp5_encoder->intf->num;
|
||||
unsigned long flags;
|
||||
|
||||
if (WARN_ON(!mdp5_encoder->enabled))
|
||||
return;
|
||||
|
||||
mdp5_ctl_set_encoder_state(ctl, false);
|
||||
mdp5_ctl_set_encoder_state(ctl, pipeline, false);
|
||||
|
||||
spin_lock_irqsave(&mdp5_encoder->intf_lock, flags);
|
||||
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 0);
|
||||
spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
|
||||
mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
|
||||
mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf));
|
||||
|
||||
/*
|
||||
* Wait for a vsync so we know the ENABLE=0 latched before
|
||||
@@ -238,7 +238,7 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder)
|
||||
* the settings changes for the new modeset (like new
|
||||
* scanout buffer) don't latch properly..
|
||||
*/
|
||||
mdp_irq_wait(&mdp5_kms->base, intf2vblank(lm, intf));
|
||||
mdp_irq_wait(&mdp5_kms->base, intf2vblank(mixer, intf));
|
||||
|
||||
bs_set(mdp5_encoder, 0);
|
||||
|
||||
@@ -250,8 +250,9 @@ static void mdp5_vid_encoder_enable(struct drm_encoder *encoder)
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
struct mdp5_ctl *ctl = mdp5_encoder->ctl;
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
int intfn = mdp5_encoder->intf.num;
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
|
||||
int intfn = intf->num;
|
||||
unsigned long flags;
|
||||
|
||||
if (WARN_ON(mdp5_encoder->enabled))
|
||||
@@ -261,9 +262,9 @@ static void mdp5_vid_encoder_enable(struct drm_encoder *encoder)
|
||||
spin_lock_irqsave(&mdp5_encoder->intf_lock, flags);
|
||||
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 1);
|
||||
spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
|
||||
mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
|
||||
mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf));
|
||||
|
||||
mdp5_ctl_set_encoder_state(ctl, true);
|
||||
mdp5_ctl_set_encoder_state(ctl, pipeline, true);
|
||||
|
||||
mdp5_encoder->enabled = true;
|
||||
}
|
||||
@@ -273,7 +274,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
|
||||
if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
|
||||
mdp5_cmd_encoder_mode_set(encoder, mode, adjusted_mode);
|
||||
@@ -284,7 +285,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
|
||||
static void mdp5_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
|
||||
if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
|
||||
mdp5_cmd_encoder_disable(encoder);
|
||||
@@ -295,7 +296,7 @@ static void mdp5_encoder_disable(struct drm_encoder *encoder)
|
||||
static void mdp5_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
|
||||
if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
|
||||
mdp5_cmd_encoder_disable(encoder);
|
||||
@@ -303,17 +304,33 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder)
|
||||
mdp5_vid_encoder_enable(encoder);
|
||||
}
|
||||
|
||||
static int mdp5_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc_state);
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
struct mdp5_ctl *ctl = mdp5_encoder->ctl;
|
||||
|
||||
mdp5_cstate->ctl = ctl;
|
||||
mdp5_cstate->pipeline.intf = intf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
|
||||
.mode_set = mdp5_encoder_mode_set,
|
||||
.disable = mdp5_encoder_disable,
|
||||
.enable = mdp5_encoder_enable,
|
||||
.atomic_check = mdp5_encoder_atomic_check,
|
||||
};
|
||||
|
||||
int mdp5_encoder_get_linecount(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
int intf = mdp5_encoder->intf.num;
|
||||
int intf = mdp5_encoder->intf->num;
|
||||
|
||||
return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf));
|
||||
}
|
||||
@@ -322,7 +339,7 @@ u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_kms *mdp5_kms = get_kms(encoder);
|
||||
int intf = mdp5_encoder->intf.num;
|
||||
int intf = mdp5_encoder->intf->num;
|
||||
|
||||
return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf));
|
||||
}
|
||||
@@ -340,7 +357,7 @@ int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder,
|
||||
return -EINVAL;
|
||||
|
||||
mdp5_kms = get_kms(encoder);
|
||||
intf_num = mdp5_encoder->intf.num;
|
||||
intf_num = mdp5_encoder->intf->num;
|
||||
|
||||
/* Switch slave encoder's TimingGen Sync mode,
|
||||
* to use the master's enable signal for the slave encoder.
|
||||
@@ -369,7 +386,7 @@ int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder,
|
||||
void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode)
|
||||
{
|
||||
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
|
||||
struct mdp5_interface *intf = &mdp5_encoder->intf;
|
||||
struct mdp5_interface *intf = mdp5_encoder->intf;
|
||||
|
||||
/* TODO: Expand this to set writeback modes too */
|
||||
if (cmd_mode) {
|
||||
@@ -385,7 +402,8 @@ void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode)
|
||||
|
||||
/* initialize encoder */
|
||||
struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
|
||||
struct mdp5_interface *intf, struct mdp5_ctl *ctl)
|
||||
struct mdp5_interface *intf,
|
||||
struct mdp5_ctl *ctl)
|
||||
{
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct mdp5_encoder *mdp5_encoder;
|
||||
@@ -399,9 +417,9 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(&mdp5_encoder->intf, intf, sizeof(mdp5_encoder->intf));
|
||||
encoder = &mdp5_encoder->base;
|
||||
mdp5_encoder->ctl = ctl;
|
||||
mdp5_encoder->intf = intf;
|
||||
|
||||
spin_lock_init(&mdp5_encoder->intf_lock);
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s)
|
||||
|
||||
/* Copy state: */
|
||||
new_state->hwpipe = mdp5_kms->state->hwpipe;
|
||||
new_state->hwmixer = mdp5_kms->state->hwmixer;
|
||||
if (mdp5_kms->smp)
|
||||
new_state->smp = mdp5_kms->state->smp;
|
||||
|
||||
@@ -165,13 +166,16 @@ static void mdp5_kms_destroy(struct msm_kms *kms)
|
||||
struct msm_gem_address_space *aspace = mdp5_kms->aspace;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mdp5_kms->num_hwmixers; i++)
|
||||
mdp5_mixer_destroy(mdp5_kms->hwmixers[i]);
|
||||
|
||||
for (i = 0; i < mdp5_kms->num_hwpipes; i++)
|
||||
mdp5_pipe_destroy(mdp5_kms->hwpipes[i]);
|
||||
|
||||
if (aspace) {
|
||||
aspace->mmu->funcs->detach(aspace->mmu,
|
||||
iommu_ports, ARRAY_SIZE(iommu_ports));
|
||||
msm_gem_address_space_destroy(aspace);
|
||||
msm_gem_address_space_put(aspace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,19 +272,14 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms)
|
||||
}
|
||||
|
||||
static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
|
||||
enum mdp5_intf_type intf_type, int intf_num,
|
||||
struct mdp5_ctl *ctl)
|
||||
struct mdp5_interface *intf,
|
||||
struct mdp5_ctl *ctl)
|
||||
{
|
||||
struct drm_device *dev = mdp5_kms->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_encoder *encoder;
|
||||
struct mdp5_interface intf = {
|
||||
.num = intf_num,
|
||||
.type = intf_type,
|
||||
.mode = MDP5_INTF_MODE_NONE,
|
||||
};
|
||||
|
||||
encoder = mdp5_encoder_init(dev, &intf, ctl);
|
||||
encoder = mdp5_encoder_init(dev, intf, ctl);
|
||||
if (IS_ERR(encoder)) {
|
||||
dev_err(dev->dev, "failed to construct encoder\n");
|
||||
return encoder;
|
||||
@@ -309,32 +308,28 @@ static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
||||
static int modeset_init_intf(struct mdp5_kms *mdp5_kms,
|
||||
struct mdp5_interface *intf)
|
||||
{
|
||||
struct drm_device *dev = mdp5_kms->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
const struct mdp5_cfg_hw *hw_cfg =
|
||||
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
||||
enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num];
|
||||
struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm;
|
||||
struct mdp5_ctl *ctl;
|
||||
struct drm_encoder *encoder;
|
||||
int ret = 0;
|
||||
|
||||
switch (intf_type) {
|
||||
case INTF_DISABLED:
|
||||
break;
|
||||
switch (intf->type) {
|
||||
case INTF_eDP:
|
||||
if (!priv->edp)
|
||||
break;
|
||||
|
||||
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
||||
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
||||
if (!ctl) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, ctl);
|
||||
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
||||
if (IS_ERR(encoder)) {
|
||||
ret = PTR_ERR(encoder);
|
||||
break;
|
||||
@@ -346,13 +341,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
||||
if (!priv->hdmi)
|
||||
break;
|
||||
|
||||
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
||||
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
||||
if (!ctl) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, ctl);
|
||||
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
||||
if (IS_ERR(encoder)) {
|
||||
ret = PTR_ERR(encoder);
|
||||
break;
|
||||
@@ -362,11 +357,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
||||
break;
|
||||
case INTF_DSI:
|
||||
{
|
||||
int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num);
|
||||
const struct mdp5_cfg_hw *hw_cfg =
|
||||
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
||||
int dsi_id = get_dsi_id_from_intf(hw_cfg, intf->num);
|
||||
|
||||
if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) {
|
||||
dev_err(dev->dev, "failed to find dsi from intf %d\n",
|
||||
intf_num);
|
||||
intf->num);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -374,13 +371,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
||||
if (!priv->dsi[dsi_id])
|
||||
break;
|
||||
|
||||
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
||||
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
||||
if (!ctl) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
encoder = construct_encoder(mdp5_kms, INTF_DSI, intf_num, ctl);
|
||||
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
||||
if (IS_ERR(encoder)) {
|
||||
ret = PTR_ERR(encoder);
|
||||
break;
|
||||
@@ -390,7 +387,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dev_err(dev->dev, "unknown intf: %d\n", intf_type);
|
||||
dev_err(dev->dev, "unknown intf: %d\n", intf->type);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -414,8 +411,8 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
* Construct encoders and modeset initialize connector devices
|
||||
* for each external display interface.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) {
|
||||
ret = modeset_init_intf(mdp5_kms, i);
|
||||
for (i = 0; i < mdp5_kms->num_intfs; i++) {
|
||||
ret = modeset_init_intf(mdp5_kms, mdp5_kms->intfs[i]);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
@@ -425,7 +422,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||
* 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_cfg->lm.count);
|
||||
num_crtcs = min(priv->num_encoders, mdp5_kms->num_hwmixers);
|
||||
|
||||
/*
|
||||
* Construct planes equaling the number of hw pipes, and CRTCs for the
|
||||
@@ -744,6 +741,7 @@ fail:
|
||||
static void mdp5_destroy(struct platform_device *pdev)
|
||||
{
|
||||
struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
if (mdp5_kms->ctlm)
|
||||
mdp5_ctlm_destroy(mdp5_kms->ctlm);
|
||||
@@ -752,6 +750,9 @@ static void mdp5_destroy(struct platform_device *pdev)
|
||||
if (mdp5_kms->cfg)
|
||||
mdp5_cfg_destroy(mdp5_kms->cfg);
|
||||
|
||||
for (i = 0; i < mdp5_kms->num_intfs; i++)
|
||||
kfree(mdp5_kms->intfs[i]);
|
||||
|
||||
if (mdp5_kms->rpm_enabled)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
@@ -829,6 +830,64 @@ static int hwpipe_init(struct mdp5_kms *mdp5_kms)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwmixer_init(struct mdp5_kms *mdp5_kms)
|
||||
{
|
||||
struct drm_device *dev = mdp5_kms->dev;
|
||||
const struct mdp5_cfg_hw *hw_cfg;
|
||||
int i, ret;
|
||||
|
||||
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
||||
|
||||
for (i = 0; i < hw_cfg->lm.count; i++) {
|
||||
struct mdp5_hw_mixer *mixer;
|
||||
|
||||
mixer = mdp5_mixer_init(&hw_cfg->lm.instances[i]);
|
||||
if (IS_ERR(mixer)) {
|
||||
ret = PTR_ERR(mixer);
|
||||
dev_err(dev->dev, "failed to construct LM%d (%d)\n",
|
||||
i, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mixer->idx = mdp5_kms->num_hwmixers;
|
||||
mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int interface_init(struct mdp5_kms *mdp5_kms)
|
||||
{
|
||||
struct drm_device *dev = mdp5_kms->dev;
|
||||
const struct mdp5_cfg_hw *hw_cfg;
|
||||
const enum mdp5_intf_type *intf_types;
|
||||
int i;
|
||||
|
||||
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
||||
intf_types = hw_cfg->intf.connect;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) {
|
||||
struct mdp5_interface *intf;
|
||||
|
||||
if (intf_types[i] == INTF_DISABLED)
|
||||
continue;
|
||||
|
||||
intf = kzalloc(sizeof(*intf), GFP_KERNEL);
|
||||
if (!intf) {
|
||||
dev_err(dev->dev, "failed to construct INTF%d\n", i);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
intf->num = i;
|
||||
intf->type = intf_types[i];
|
||||
intf->mode = MDP5_INTF_MODE_NONE;
|
||||
intf->idx = mdp5_kms->num_intfs;
|
||||
mdp5_kms->intfs[mdp5_kms->num_intfs++] = intf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
@@ -929,6 +988,14 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = hwmixer_init(mdp5_kms);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = interface_init(mdp5_kms);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* set uninit-ed kms */
|
||||
priv->kms = &mdp5_kms->base.base;
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
#include "mdp/mdp_kms.h"
|
||||
#include "mdp5_cfg.h" /* must be included before mdp5.xml.h */
|
||||
#include "mdp5.xml.h"
|
||||
#include "mdp5_ctl.h"
|
||||
#include "mdp5_pipe.h"
|
||||
#include "mdp5_mixer.h"
|
||||
#include "mdp5_ctl.h"
|
||||
#include "mdp5_smp.h"
|
||||
|
||||
struct mdp5_state;
|
||||
@@ -39,6 +40,12 @@ struct mdp5_kms {
|
||||
unsigned num_hwpipes;
|
||||
struct mdp5_hw_pipe *hwpipes[SSPP_MAX];
|
||||
|
||||
unsigned num_hwmixers;
|
||||
struct mdp5_hw_mixer *hwmixers[8];
|
||||
|
||||
unsigned num_intfs;
|
||||
struct mdp5_interface *intfs[5];
|
||||
|
||||
struct mdp5_cfg_handler *cfg;
|
||||
uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */
|
||||
|
||||
@@ -83,6 +90,7 @@ struct mdp5_kms {
|
||||
*/
|
||||
struct mdp5_state {
|
||||
struct mdp5_hw_pipe_state hwpipe;
|
||||
struct mdp5_hw_mixer_state hwmixer;
|
||||
struct mdp5_smp_state smp;
|
||||
};
|
||||
|
||||
@@ -96,6 +104,7 @@ struct mdp5_plane_state {
|
||||
struct drm_plane_state base;
|
||||
|
||||
struct mdp5_hw_pipe *hwpipe;
|
||||
struct mdp5_hw_pipe *r_hwpipe; /* right hwpipe */
|
||||
|
||||
/* aligned with property */
|
||||
uint8_t premultiplied;
|
||||
@@ -108,6 +117,28 @@ struct mdp5_plane_state {
|
||||
#define to_mdp5_plane_state(x) \
|
||||
container_of(x, struct mdp5_plane_state, base)
|
||||
|
||||
struct mdp5_pipeline {
|
||||
struct mdp5_interface *intf;
|
||||
struct mdp5_hw_mixer *mixer;
|
||||
struct mdp5_hw_mixer *r_mixer; /* right mixer */
|
||||
};
|
||||
|
||||
struct mdp5_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
|
||||
struct mdp5_ctl *ctl;
|
||||
struct mdp5_pipeline pipeline;
|
||||
|
||||
/* these are derivatives of intf/mixer state in mdp5_pipeline */
|
||||
u32 vblank_irqmask;
|
||||
u32 err_irqmask;
|
||||
u32 pp_done_irqmask;
|
||||
|
||||
bool cmd_mode;
|
||||
};
|
||||
#define to_mdp5_crtc_state(x) \
|
||||
container_of(x, struct mdp5_crtc_state, base)
|
||||
|
||||
enum mdp5_intf_mode {
|
||||
MDP5_INTF_MODE_NONE = 0,
|
||||
|
||||
@@ -121,6 +152,7 @@ enum mdp5_intf_mode {
|
||||
};
|
||||
|
||||
struct mdp5_interface {
|
||||
int idx;
|
||||
int num; /* display interface number */
|
||||
enum mdp5_intf_type type;
|
||||
enum mdp5_intf_mode mode;
|
||||
@@ -128,11 +160,11 @@ struct mdp5_interface {
|
||||
|
||||
struct mdp5_encoder {
|
||||
struct drm_encoder base;
|
||||
struct mdp5_interface intf;
|
||||
spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */
|
||||
bool enabled;
|
||||
uint32_t bsc;
|
||||
|
||||
struct mdp5_interface *intf;
|
||||
struct mdp5_ctl *ctl;
|
||||
};
|
||||
#define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base)
|
||||
@@ -197,8 +229,8 @@ static inline uint32_t intf2err(int intf_num)
|
||||
}
|
||||
}
|
||||
|
||||
#define GET_PING_PONG_ID(layer_mixer) ((layer_mixer == 5) ? 3 : layer_mixer)
|
||||
static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
|
||||
static inline uint32_t intf2vblank(struct mdp5_hw_mixer *mixer,
|
||||
struct mdp5_interface *intf)
|
||||
{
|
||||
/*
|
||||
* In case of DSI Command Mode, the Ping Pong's read pointer IRQ
|
||||
@@ -208,7 +240,7 @@ static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
|
||||
|
||||
if ((intf->type == INTF_DSI) &&
|
||||
(intf->mode == MDP5_INTF_DSI_MODE_COMMAND))
|
||||
return MDP5_IRQ_PING_PONG_0_RD_PTR << GET_PING_PONG_ID(lm);
|
||||
return MDP5_IRQ_PING_PONG_0_RD_PTR << mixer->pp;
|
||||
|
||||
if (intf->type == INTF_WB)
|
||||
return MDP5_IRQ_WB_2_DONE;
|
||||
@@ -222,9 +254,9 @@ static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t lm2ppdone(int lm)
|
||||
static inline uint32_t lm2ppdone(struct mdp5_hw_mixer *mixer)
|
||||
{
|
||||
return MDP5_IRQ_PING_PONG_0_DONE << GET_PING_PONG_ID(lm);
|
||||
return MDP5_IRQ_PING_PONG_0_DONE << mixer->pp;
|
||||
}
|
||||
|
||||
int mdp5_disable(struct mdp5_kms *mdp5_kms);
|
||||
@@ -243,15 +275,16 @@ void mdp5_irq_domain_fini(struct mdp5_kms *mdp5_kms);
|
||||
|
||||
uint32_t mdp5_plane_get_flush(struct drm_plane *plane);
|
||||
enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
|
||||
enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane);
|
||||
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
|
||||
enum drm_plane_type type);
|
||||
|
||||
struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc);
|
||||
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
|
||||
|
||||
int mdp5_crtc_get_lm(struct drm_crtc *crtc);
|
||||
void mdp5_crtc_set_pipeline(struct drm_crtc *crtc,
|
||||
struct mdp5_interface *intf, struct mdp5_ctl *ctl);
|
||||
struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc);
|
||||
struct mdp5_pipeline *mdp5_crtc_get_pipeline(struct drm_crtc *crtc);
|
||||
void mdp5_crtc_set_pipeline(struct drm_crtc *crtc);
|
||||
void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc);
|
||||
struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
|
||||
struct drm_plane *plane,
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mdp5_kms.h"
|
||||
|
||||
/*
|
||||
* As of now, there are only 2 combinations possible for source split:
|
||||
*
|
||||
* Left | Right
|
||||
* -----|------
|
||||
* LM0 | LM1
|
||||
* LM2 | LM5
|
||||
*
|
||||
*/
|
||||
static int lm_right_pair[] = { 1, -1, 5, -1, -1, -1 };
|
||||
|
||||
static int get_right_pair_idx(struct mdp5_kms *mdp5_kms, int lm)
|
||||
{
|
||||
int i;
|
||||
int pair_lm;
|
||||
|
||||
pair_lm = lm_right_pair[lm];
|
||||
if (pair_lm < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
|
||||
struct mdp5_hw_mixer *mixer = mdp5_kms->hwmixers[i];
|
||||
|
||||
if (mixer->lm == pair_lm)
|
||||
return mixer->idx;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct msm_drm_private *priv = s->dev->dev_private;
|
||||
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
|
||||
struct mdp5_state *state = mdp5_get_state(s);
|
||||
struct mdp5_hw_mixer_state *new_state;
|
||||
int i;
|
||||
|
||||
if (IS_ERR(state))
|
||||
return PTR_ERR(state);
|
||||
|
||||
new_state = &state->hwmixer;
|
||||
|
||||
for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
|
||||
struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i];
|
||||
|
||||
/*
|
||||
* skip if already in-use by a different CRTC. If there is a
|
||||
* mixer already assigned to this CRTC, it means this call is
|
||||
* a request to get an additional right mixer. Assume that the
|
||||
* existing mixer is the 'left' one, and try to see if we can
|
||||
* get its corresponding 'right' pair.
|
||||
*/
|
||||
if (new_state->hwmixer_to_crtc[cur->idx] &&
|
||||
new_state->hwmixer_to_crtc[cur->idx] != crtc)
|
||||
continue;
|
||||
|
||||
/* skip if doesn't support some required caps: */
|
||||
if (caps & ~cur->caps)
|
||||
continue;
|
||||
|
||||
if (r_mixer) {
|
||||
int pair_idx;
|
||||
|
||||
pair_idx = get_right_pair_idx(mdp5_kms, cur->lm);
|
||||
if (pair_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (new_state->hwmixer_to_crtc[pair_idx])
|
||||
continue;
|
||||
|
||||
*r_mixer = mdp5_kms->hwmixers[pair_idx];
|
||||
}
|
||||
|
||||
/*
|
||||
* prefer a pair-able LM over an unpairable one. We can
|
||||
* switch the CRTC from Normal mode to Source Split mode
|
||||
* without requiring a full modeset if we had already
|
||||
* assigned this CRTC a pair-able LM.
|
||||
*
|
||||
* TODO: There will be assignment sequences which would
|
||||
* result in the CRTC requiring a full modeset, even
|
||||
* if we have the LM resources to prevent it. For a platform
|
||||
* with a few displays, we don't run out of pair-able LMs
|
||||
* so easily. For now, ignore the possibility of requiring
|
||||
* a full modeset.
|
||||
*/
|
||||
if (!(*mixer) || cur->caps & MDP_LM_CAP_PAIR)
|
||||
*mixer = cur;
|
||||
}
|
||||
|
||||
if (!(*mixer))
|
||||
return -ENOMEM;
|
||||
|
||||
if (r_mixer && !(*r_mixer))
|
||||
return -ENOMEM;
|
||||
|
||||
DBG("assigning Layer Mixer %d to crtc %s", (*mixer)->lm, crtc->name);
|
||||
|
||||
new_state->hwmixer_to_crtc[(*mixer)->idx] = crtc;
|
||||
if (r_mixer) {
|
||||
DBG("assigning Right Layer Mixer %d to crtc %s", (*r_mixer)->lm,
|
||||
crtc->name);
|
||||
new_state->hwmixer_to_crtc[(*r_mixer)->idx] = crtc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
|
||||
{
|
||||
struct mdp5_state *state = mdp5_get_state(s);
|
||||
struct mdp5_hw_mixer_state *new_state = &state->hwmixer;
|
||||
|
||||
if (!mixer)
|
||||
return;
|
||||
|
||||
if (WARN_ON(!new_state->hwmixer_to_crtc[mixer->idx]))
|
||||
return;
|
||||
|
||||
DBG("%s: release from crtc %s", mixer->name,
|
||||
new_state->hwmixer_to_crtc[mixer->idx]->name);
|
||||
|
||||
new_state->hwmixer_to_crtc[mixer->idx] = NULL;
|
||||
}
|
||||
|
||||
void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer)
|
||||
{
|
||||
kfree(mixer);
|
||||
}
|
||||
|
||||
static const char * const mixer_names[] = {
|
||||
"LM0", "LM1", "LM2", "LM3", "LM4", "LM5",
|
||||
};
|
||||
|
||||
struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm)
|
||||
{
|
||||
struct mdp5_hw_mixer *mixer;
|
||||
|
||||
mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
|
||||
if (!mixer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mixer->name = mixer_names[lm->id];
|
||||
mixer->lm = lm->id;
|
||||
mixer->caps = lm->caps;
|
||||
mixer->pp = lm->pp;
|
||||
mixer->dspp = lm->dspp;
|
||||
mixer->flush_mask = mdp_ctl_flush_mask_lm(lm->id);
|
||||
|
||||
return mixer;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MDP5_LM_H__
|
||||
#define __MDP5_LM_H__
|
||||
|
||||
/* represents a hw Layer Mixer, one (or more) is dynamically assigned to a crtc */
|
||||
struct mdp5_hw_mixer {
|
||||
int idx;
|
||||
|
||||
const char *name;
|
||||
|
||||
int lm; /* the LM instance # */
|
||||
uint32_t caps;
|
||||
int pp;
|
||||
int dspp;
|
||||
|
||||
uint32_t flush_mask; /* used to commit LM registers */
|
||||
};
|
||||
|
||||
/* global atomic state of assignment between CRTCs and Layer Mixers: */
|
||||
struct mdp5_hw_mixer_state {
|
||||
struct drm_crtc *hwmixer_to_crtc[8];
|
||||
};
|
||||
|
||||
struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm);
|
||||
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);
|
||||
|
||||
#endif /* __MDP5_LM_H__ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user