Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67a3f79e authored by Alan Kwong's avatar Alan Kwong
Browse files

drm/msm/sde: add bus bandwidth control for crtc



Add bus bandwidth control (on/off) for crtc around atomic
commit and frame done event. To support video mode interface,
also add bandwidth control around encoder enable/disable.

CRs-Fixed: 2005348
Change-Id: I90ef372aad4a20040602a468393e8a5cd8f08201
Signed-off-by: default avatarAlan Kwong <akwong@codeaurora.org>
parent 3479e4d0
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -90,8 +90,6 @@ Optional properties:
				-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
				-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
				-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
- qcom,sde-reg-bus:		Property to provide Bus scaling for register access for
				mdss blocks.
- qcom,sde-sspp-src-size:	A u32 value indicates the address range for each sspp.
- qcom,sde-mixer-size:		A u32 value indicates the address range for each mixer.
- qcom,sde-ctl-size:		A u32 value indicates the address range for each ctl.
@@ -247,6 +245,21 @@ Optional properties:
- qcom,sde-reg-dma-version:	Version of the reg dma hardware block.
- qcom,sde-reg-dma-trigger-off: Offset of the lut dma trigger reg from "mdp_phys"
				defined in reg property.
- qcom,sde-dram-channels:	This represents the number of channels in the
				Bus memory controller.
- qcom,sde-num-nrt-paths:	Integer property represents the number of non-realtime
				paths in each Bus Scaling Usecase. This value depends on
				number of AXI ports that are dedicated to non-realtime VBIF
				for particular chipset.
				These paths must be defined after rt-paths in
				"qcom,msm-bus,vectors-KBps" vector request.

Bus Scaling Subnodes:
- qcom,sde-reg-bus:		Property to provide Bus scaling for register access for
				mdss blocks.
- qcom,sde-data-bus:		Property to provide Bus scaling for data bus access for
				mdss blocks.

Bus Scaling Data:
- qcom,msm-bus,name:		String property describing client name.
- qcom,msm-bus,num-cases:	This is the number of Bus Scaling use cases
@@ -415,6 +428,9 @@ Example:
    qcom,sde-vbif-dynamic-ot-wr-limit = <62208000 2>,
        <124416000 4>, <248832000 16>;

    qcom,sde-dram-channels = <2>;
    qcom,sde-num-nrt-paths = <1>;

    qcom,sde-sspp-vig-blocks {
        qcom,sde-vig-csc-off = <0x320>;
        qcom,sde-vig-qseed-off = <0x200>;
@@ -466,6 +482,18 @@ Example:
        };
    };

    qcom,sde-data-bus {
        qcom,msm-bus,name = "mdss_sde";
        qcom,msm-bus,num-cases = <3>;
        qcom,msm-bus,num-paths = <3>;
        qcom,msm-bus,vectors-KBps =
            <22 512 0 0>, <23 512 0 0>, <25 512 0 0>,
            <22 512 0 6400000>, <23 512 0 6400000>,
                <25 512 0 6400000>,
            <22 512 0 6400000>, <23 512 0 6400000>,
                <25 512 0 6400000>;
    };

    qcom,sde-reg-bus {
        /* Reg Bus Scale Settings */
        qcom,msm-bus,name = "mdss_reg";
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ msm_drm-y := \
	sde/sde_encoder_phys_cmd.o \
	sde/sde_irq.o \
	sde/sde_core_irq.o \
	sde/sde_core_perf.o \
	sde/sde_rm.o \
	sde/sde_kms_utils.o \
	sde/sde_kms.o \
+140 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016, 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 and
 * only 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.
 */

#define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__

#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/sort.h>
#include <linux/clk.h>
#include <linux/bitmap.h>

#include "msm_prop.h"

#include "sde_kms.h"
#include "sde_fence.h"
#include "sde_formats.h"
#include "sde_hw_sspp.h"
#include "sde_trace.h"
#include "sde_crtc.h"
#include "sde_plane.h"
#include "sde_encoder.h"
#include "sde_wb.h"
#include "sde_core_perf.h"
#include "sde_trace.h"

#ifdef CONFIG_DEBUG_FS

static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
{
	debugfs_remove_recursive(perf->debugfs_root);
	perf->debugfs_root = NULL;
}

static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
		struct dentry *parent)
{
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;

	priv = perf->dev->dev_private;
	if (!priv || !priv->kms) {
		SDE_ERROR("invalid KMS reference\n");
		return -EINVAL;
	}

	sde_kms = to_sde_kms(priv->kms);

	perf->debugfs_root = debugfs_create_dir("core_perf", parent);
	if (!perf->debugfs_root) {
		SDE_ERROR("failed to create core perf debugfs\n");
		return -EINVAL;
	}

	debugfs_create_u64("max_core_clk_rate", 0644, perf->debugfs_root,
			&perf->max_core_clk_rate);
	debugfs_create_u32("core_clk_rate", 0644, perf->debugfs_root,
			&perf->core_clk_rate);

	return 0;
}
#else
static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
{
}

static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
		struct dentry *parent)
{
	return 0;
}
#endif

void sde_core_perf_destroy(struct sde_core_perf *perf)
{
	if (!perf) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	sde_debugfs_core_perf_destroy(perf);
	perf->max_core_clk_rate = 0;
	perf->core_clk = NULL;
	mutex_destroy(&perf->perf_lock);
	perf->clk_name = NULL;
	perf->phandle = NULL;
	perf->catalog = NULL;
	perf->dev = NULL;
}

int sde_core_perf_init(struct sde_core_perf *perf,
		struct drm_device *dev,
		struct sde_mdss_cfg *catalog,
		struct sde_power_handle *phandle,
		struct sde_power_client *pclient,
		char *clk_name,
		struct dentry *debugfs_parent)
{
	if (!perf || !catalog || !phandle || !pclient ||
			!clk_name || !debugfs_parent) {
		SDE_ERROR("invalid parameters\n");
		return -EINVAL;
	}

	perf->dev = dev;
	perf->catalog = catalog;
	perf->phandle = phandle;
	perf->pclient = pclient;
	perf->clk_name = clk_name;
	mutex_init(&perf->perf_lock);

	perf->core_clk = sde_power_clk_get_clk(phandle, clk_name);
	if (!perf->core_clk) {
		SDE_ERROR("invalid core clk\n");
		goto err;
	}

	perf->max_core_clk_rate = sde_power_clk_get_max_rate(phandle, clk_name);
	if (!perf->max_core_clk_rate) {
		SDE_ERROR("invalid max core clk rate\n");
		goto err;
	}

	sde_debugfs_core_perf_init(perf, debugfs_parent);

	return 0;

err:
	sde_core_perf_destroy(perf);
	return -ENODEV;
}
+73 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016, 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 and
 * only 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.
 */

#ifndef __SDE_CORE_PERF_H__
#define __SDE_CORE_PERF_H__

#include <linux/types.h>
#include <linux/dcache.h>
#include <linux/mutex.h>

#include "sde_hw_catalog.h"
#include "sde_power_handle.h"

/**
 * struct sde_core_perf - definition of core performance context
 * @dev: Pointer to drm device
 * @debugfs_root: top level debug folder
 * @perf_lock: serialization lock for this context
 * @catalog: Pointer to catalog configuration
 * @phandle: Pointer to power handler
 * @pclient: Pointer to power client
 * @clk_name: core clock name
 * @core_clk: Pointer to core clock structure
 * @core_clk_rate: current core clock rate
 * @max_core_clk_rate: maximum allowable core clock rate
 */
struct sde_core_perf {
	struct drm_device *dev;
	struct dentry *debugfs_root;
	struct mutex perf_lock;
	struct sde_mdss_cfg *catalog;
	struct sde_power_handle *phandle;
	struct sde_power_client *pclient;
	char *clk_name;
	struct clk *core_clk;
	u32 core_clk_rate;
	u64 max_core_clk_rate;
};

/**
 * sde_core_perf_destroy - destroy the given core performance context
 * @perf: Pointer to core performance context
 */
void sde_core_perf_destroy(struct sde_core_perf *perf);

/**
 * sde_core_perf_init - initialize the given core performance context
 * @perf: Pointer to core performance context
 * @dev: Pointer to drm device
 * @catalog: Pointer to catalog
 * @phandle: Pointer to power handle
 * @pclient: Pointer to power client
 * @clk_name: core clock name
 * @debugfs_parent: Pointer to parent debugfs
 */
int sde_core_perf_init(struct sde_core_perf *perf,
		struct drm_device *dev,
		struct sde_mdss_cfg *catalog,
		struct sde_power_handle *phandle,
		struct sde_power_client *pclient,
		char *clk_name,
		struct dentry *debugfs_parent);

#endif /* __SDE_CORE_PERF_H__ */
+59 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include "sde_color_processing.h"
#include "sde_encoder.h"
#include "sde_connector.h"
#include "sde_power_handle.h"

/* default input fence timeout, in ms */
#define SDE_CRTC_INPUT_FENCE_TIMEOUT    2000
@@ -428,6 +429,12 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
				cstate->is_rt = true;
		}

	if (cstate->num_connectors > 0 && cstate->connectors[0]->encoder)
		cstate->intf_mode = sde_encoder_get_intf_mode(
				cstate->connectors[0]->encoder);
	else
		cstate->intf_mode = INTF_MODE_NONE;

	/* prepare main output fence */
	sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -486,6 +493,7 @@ static void sde_crtc_vblank_cb(void *data)

static void sde_crtc_frame_event_work(struct kthread_work *work)
{
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	struct drm_crtc *crtc;
	struct sde_crtc *sde_crtc;
@@ -511,6 +519,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
		SDE_ERROR("invalid kms handle\n");
		return;
	}
	priv = sde_kms->dev->dev_private;

	SDE_DEBUG("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event,
			ktime_to_ns(fevent->ts));
@@ -531,6 +540,8 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
					crtc->base.id,
					ktime_to_ns(fevent->ts));
			SDE_EVT32(DRMID(crtc), fevent->event, 1);
			sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
					sde_kms->core_client, false);
		} else {
			SDE_EVT32(DRMID(crtc), fevent->event, 2);
		}
@@ -951,6 +962,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
	struct drm_encoder *encoder;
	struct drm_device *dev;
	struct sde_crtc *sde_crtc;
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;

	if (!crtc) {
		SDE_ERROR("invalid argument\n");
@@ -958,6 +971,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
	}
	dev = crtc->dev;
	sde_crtc = to_sde_crtc(crtc);
	sde_kms = _sde_crtc_get_kms(crtc);
	priv = sde_kms->dev->dev_private;

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
@@ -980,6 +995,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
		/* acquire bandwidth and other resources */
		SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), 1);
		sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
				sde_kms->core_client, true);
	} else {
		SDE_DEBUG("crtc%d commit\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), 2);
@@ -1068,14 +1085,18 @@ static void sde_crtc_reset(struct drm_crtc *crtc)

static void sde_crtc_disable(struct drm_crtc *crtc)
{
	struct msm_drm_private *priv;
	struct sde_crtc *sde_crtc;
	struct drm_encoder *encoder;
	struct sde_kms *sde_kms;

	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return;
	}
	sde_crtc = to_sde_crtc(crtc);
	sde_kms = _sde_crtc_get_kms(crtc);
	priv = sde_kms->dev->dev_private;

	SDE_DEBUG("crtc%d\n", crtc->base.id);

@@ -1100,6 +1121,8 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
		SDE_ERROR("crtc%d invalid frame pending\n",
				crtc->base.id);
		SDE_EVT32(DRMID(crtc));
		sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
				sde_kms->core_client, false);
		atomic_set(&sde_crtc->frame_pending, 0);
	}

@@ -1598,6 +1621,7 @@ static int sde_crtc_atomic_get_property(struct drm_crtc *crtc,
	return ret;
}

#ifdef CONFIG_DEBUG_FS
static int _sde_debugfs_status_show(struct seq_file *s, void *data)
{
	struct sde_crtc *sde_crtc;
@@ -1715,6 +1739,7 @@ static int _sde_debugfs_status_open(struct inode *inode, struct file *file)
{
	return single_open(file, _sde_debugfs_status_show, inode->i_private);
}
#endif

static const struct drm_crtc_funcs sde_crtc_funcs = {
	.set_config = drm_atomic_helper_set_config,
@@ -1737,6 +1762,33 @@ static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
	.atomic_flush = sde_crtc_atomic_flush,
};

#ifdef CONFIG_DEBUG_FS
#define DEFINE_SDE_DEBUGFS_SEQ_FOPS(__prefix)				\
static int __prefix ## _open(struct inode *inode, struct file *file)	\
{									\
	return single_open(file, __prefix ## _show, inode->i_private);	\
}									\
static const struct file_operations __prefix ## _fops = {		\
	.owner = THIS_MODULE,						\
	.open = __prefix ## _open,					\
	.release = single_release,					\
	.read = seq_read,						\
	.llseek = seq_lseek,						\
}

static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
{
	struct drm_crtc *crtc = (struct drm_crtc *) s->private;
	struct sde_crtc_state *cstate = to_sde_crtc_state(crtc->state);

	seq_printf(s, "num_connectors: %d\n", cstate->num_connectors);
	seq_printf(s, "is_rt: %d\n", cstate->is_rt);
	seq_printf(s, "intf_mode: %d\n", cstate->intf_mode);

	return 0;
}
DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_crtc_debugfs_state);

static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
		struct sde_kms *sde_kms)
{
@@ -1746,6 +1798,7 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
		.llseek =	seq_lseek,
		.release =	single_release,
	};

	if (sde_crtc && sde_kms) {
		sde_crtc->debugfs_root = debugfs_create_dir(sde_crtc->name,
				sde_debugfs_get_root(sde_kms));
@@ -1757,6 +1810,12 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
		}
	}
}
#else
static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
		struct sde_kms *sde_kms)
{
}
#endif

/* initialize crtc */
struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
Loading