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

Commit e14b3005 authored by Abhijit Kulkarni's avatar Abhijit Kulkarni Committed by Krishna Srinivas Kundurthi
Browse files

drm/msm/sde: add resource manager to enable dual dsi



Add resource manager for retrieving the control paths and layer
mixers. Encoder and CRTC use this to get the hw driver contexts
for those blocks.

Change-Id: Id6789ef24616197a295bcb5687a0de659cc11e5d
Signed-off-by: default avatarAbhijit Kulkarni <kabhijit@codeaurora.org>
Signed-off-by: default avatarKrishna Srinivas Kundurthi <kskund@codeaurora.org>
parent f8846e93
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ msm-y := \
	sde/sde_encoder_phys_vid.o \
	sde/sde_encoder_phys_cmd.o \
	sde/sde_irq.o \
	sde/sde_kms_utils.o \
	sde/sde_kms.o \
	sde/sde_plane.o \
	msm_atomic.o \
+174 −108
Original line number Diff line number Diff line
@@ -19,40 +19,11 @@
#include "sde_kms.h"
#include "sde_hw_lm.h"
#include "sde_hw_mdp_ctl.h"
#include "sde_crtc.h"

#define CRTC_DUAL_MIXERS	2
#define PENDING_FLIP		2

#define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages)

struct sde_crtc_mixer {
	struct sde_hw_dspp  *hw_dspp;
	struct sde_hw_mixer *hw_lm;
	struct sde_hw_ctl   *hw_ctl;
	u32 flush_mask;
};

struct sde_crtc {
	struct drm_crtc base;
	char name[8];
	struct drm_plane *plane;
	struct drm_plane *planes[8];
	struct drm_encoder *encoder;
	int id;
	bool enabled;

	spinlock_t lm_lock;	/* protect registers */

	/* HW Resources reserved for the crtc */
	u32  num_ctls;
	u32  num_mixers;
	struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS];

	/*if there is a pending flip, these will be non-null */
	struct drm_pending_vblank_event *event;
};

#define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
#define CTL(i)       (CTL_0 + (i))
#define LM(i)        (LM_0  + (i))
#define INTF(i)      (INTF_0 + (i))

static struct sde_kms *get_kms(struct drm_crtc *crtc)
{
@@ -60,89 +31,91 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc)
	return to_sde_kms(priv->kms);
}

static inline struct sde_hw_ctl *sde_crtc_rm_get_ctl_path(enum sde_ctl idx,
		void __iomem *addr,
		struct sde_mdss_cfg *m)
{
	/*
	 * This module keeps track of the requested hw resources state,
	 * if the requested resource is being used it returns NULL,
	 * otherwise it returns the hw driver struct
	 */
	return sde_hw_ctl_init(idx, addr, m);
}

static inline struct sde_hw_mixer *sde_crtc_rm_get_mixer(enum sde_lm idx,
		void __iomem *addr,
		struct sde_mdss_cfg *m)
{
	/*
	 * This module keeps track of the requested hw resources state,
	 * if the requested resource is being used it returns NULL,
	 * otherwise it returns the hw driver struct
	 */
	return sde_hw_lm_init(idx, addr, m);
}

static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
		struct drm_encoder *encoder)
{
	/*
	 * Assign CRTC resources
	 * num_ctls;
	 * num_mixers;
	 * sde_lm mixer[CRTC_MAX_PIPES];
	 * sde_ctl ctl[CRTC_MAX_PIPES];
	 */
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct sde_kms *kms = get_kms(crtc);
	enum sde_lm lm_id[CRTC_DUAL_MIXERS];
	enum sde_ctl ctl_id[CRTC_DUAL_MIXERS];
	int i;

	if (!kms) {
		DBG("[%s] invalid kms\n", __func__);
	struct sde_kms *sde_kms = get_kms(crtc);
	struct sde_encoder_hw_resources enc_hw_res;
	const struct sde_hw_res_map *plat_hw_res_map;
	enum sde_lm unused_lm_id[CRTC_DUAL_MIXERS] = {0};
	enum sde_lm lm_idx;
	int i, count = 0;

	if (!sde_kms) {
		DBG("[%s] invalid kms", __func__);
		return -EINVAL;
	}

	if (!kms->mmio)
	if (!sde_kms->mmio)
		return -EINVAL;

	/*
	 * simple check validate against catalog
	 */
	sde_crtc->num_ctls = 1;
	sde_crtc->num_mixers = 1;
	ctl_id[0] = CTL_0;
	lm_id[0] = LM_0;

	/*
	 * need to also enable MDP core clock and AHB CLK
	 * before touching HW driver
	 */
	DBG("%s Enable clocks\n", __func__);
	sde_enable(kms);
	for (i = 0; i < sde_crtc->num_ctls; i++) {
		sde_crtc->mixer[i].hw_ctl = sde_crtc_rm_get_ctl_path(ctl_id[i],
				kms->mmio, kms->catalog);
		if (!sde_crtc->mixer[i].hw_ctl) {
			DBG("[%s], Invalid ctl_path", __func__);
			return -EACCES;
	/* Get unused LMs */
	for (i = 0; i < sde_kms->catalog->mixer_count; i++) {
		if (!sde_rm_get_mixer(sde_kms, LM(i))) {
			unused_lm_id[count++] = LM(i);
			if (count == CRTC_DUAL_MIXERS)
				break;
		}
	}

	for (i = 0; i < sde_crtc->num_mixers; i++) {
		sde_crtc->mixer[i].hw_lm = sde_crtc_rm_get_mixer(lm_id[i],
				kms->mmio, kms->catalog);
		if (!sde_crtc->mixer[i].hw_lm) {
	/* query encoder resources */
	sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res);

	/* parse encoder hw resources, find CTL paths */
	for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) {
		WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS);
		if (enc_hw_res.ctls[i]) {
			struct sde_crtc_mixer *mixer  =
				&sde_crtc->mixer[sde_crtc->num_ctls];
			mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i);
			if (IS_ERR_OR_NULL(mixer->hw_ctl)) {
				DBG("[%s], Invalid ctl_path", __func__);
				return -EACCES;
			}
			sde_crtc->num_ctls++;
		}
	}

	/* shortcut this process if encoder has no ctl paths */
	if (!sde_crtc->num_ctls)
		return 0;

	/*
	 * need to disable MDP core clock and AHB CLK
	 * Get default LMs if specified in platform config
	 * other wise acquire the free LMs
	 */
	sde_disable(kms);
	for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) {
		if (enc_hw_res.intfs[i]) {
			struct sde_crtc_mixer *mixer  =
				&sde_crtc->mixer[sde_crtc->num_mixers];
			plat_hw_res_map = sde_rm_get_res_map(sde_kms, i);

			lm_idx = plat_hw_res_map->lm;
			if (!lm_idx)
				lm_idx = unused_lm_id[sde_crtc->num_mixers];

			DBG("Acquiring LM %d", lm_idx);
			mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx);
			if (IS_ERR_OR_NULL(mixer->hw_lm)) {
				DBG("[%s], Invalid mixer", __func__);
				return -EACCES;
			}
			/* interface info */
			mixer->intf_idx = i;
			mixer->mode = enc_hw_res.intfs[i];
			sde_crtc->num_mixers++;
		}
	}

	DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ",
			sde_crtc->num_ctls, sde_crtc->num_mixers,
			sde_crtc->mixer[0].hw_lm->idx,
			sde_crtc->mixer[0].hw_ctl->idx);
	if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS)
		DBG("lm[1] %d, ctl[1], %d",
			sde_crtc->mixer[1].hw_lm->idx,
			sde_crtc->mixer[1].hw_ctl->idx);
	return 0;
}

@@ -278,6 +251,7 @@ static void blend_setup(struct drm_crtc *crtc)
	unsigned long flags;
	int i, j, plane_cnt = 0;

	DBG("");
	spin_lock_irqsave(&sde_crtc->lm_lock, flags);

	/* ctl could be reserved already */
@@ -353,10 +327,104 @@ out:
	spin_unlock_irqrestore(&sde_crtc->lm_lock, flags);
}

/* if file!=NULL, this is preclose potential cancel-flip path */
static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct drm_pending_vblank_event *event;
	unsigned long flags;

	spin_lock_irqsave(&dev->event_lock, flags);
	event = sde_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)) {
			sde_crtc->event = NULL;
			DBG("%s: send event: %pK", sde_crtc->name, event);
			drm_send_vblank_event(dev, sde_crtc->id, event);
		}
	}
	spin_unlock_irqrestore(&dev->event_lock, flags);
}

static void sde_crtc_vblank_cb(void *data)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	unsigned pending;

	/* unregister callback */
	sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL);

	pending = atomic_xchg(&sde_crtc->pending, 0);

	if (pending & PENDING_FLIP)
		complete_flip(crtc, NULL);
}

static int frame_flushed(struct sde_crtc *sde_crtc)
{
	struct vsync_info vsync;

	/* encoder get vsync_info */
	/* if frame_count does not match frame is flushed */
	sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync);

	return (vsync.frame_count & sde_crtc->vsync_count);

}

void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	u32 pending;
	int i, ret;

	/* ref count the vblank event */
	ret = drm_crtc_vblank_get(crtc);
	if (ret)
		return;

	/* register callback */
	sde_encoder_register_vblank_callback(sde_crtc->encoder,
			sde_crtc_vblank_cb,
			(void *)crtc);

	/* wait */
	pending = atomic_read(&sde_crtc->pending);
	if (pending & PENDING_FLIP) {
		wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
				(frame_flushed(sde_crtc) != 0),
				msecs_to_jiffies(CRTC_MAX_WAIT_ONE_FRAME));
		if (ret <= 0)
			dev_warn(dev->dev, "vblank time out, crtc=%d\n",
					sde_crtc->id);
	}

	for (i = 0; i < sde_crtc->num_ctls; i++)
		sde_crtc->mixer[i].flush_mask = 0;

	/* release */
	drm_crtc_vblank_put(crtc);
}

static void request_pending(struct drm_crtc *crtc, u32 pending)
{
	DBG("");
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct vsync_info vsync;

	/* request vsync info, cache the current frame count */
	sde_encoder_get_vsync_info(sde_crtc->encoder, &vsync);
	sde_crtc->vsync_count = vsync.frame_count;

	atomic_or(pending, &sde_crtc->pending);
}

/**
 * Flush the CTL PATH
 */
@@ -369,14 +437,12 @@ static u32 crtc_flush_all(struct drm_crtc *crtc)
	DBG("");

	for (i = 0; i < sde_crtc->num_ctls; i++) {
		/*
		 * Query flush_mask from encoder
		 * and append to the ctl_path flush_mask
		 */
		ctl = sde_crtc->mixer[i].hw_ctl;
		ctl->ops.get_bitmask_intf(ctl,
				&(sde_crtc->mixer[i].flush_mask),
				INTF_1);
				sde_crtc->mixer[i].intf_idx);
		DBG("Flushing CTL_ID %d, flush_mask %x", ctl->idx,
				sde_crtc->mixer[i].flush_mask);
		ctl->ops.setup_flush(ctl,
				sde_crtc->mixer[i].flush_mask);
	}
@@ -425,7 +491,7 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
	struct drm_device *dev = crtc->dev;
	unsigned long flags;

	DBG("");
	DBG("%s: event: %pK", sde_crtc->name, crtc->state->event);

	WARN_ON(sde_crtc->event);

@@ -605,6 +671,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
		return ERR_PTR(-EINVAL);
	 }

	DBG("%s: Successfully initialized crtc\n", __func__);
	DBG("%s: Successfully initialized crtc", __func__);
	return crtc;
}
+79 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015-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_CRTC_H_
#define _SDE_CRTC_H_

#include "drm_crtc.h"

#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)

#define CRTC_DUAL_MIXERS	2
#define PENDING_FLIP		2
/* worst case one frame wait time based on 30 FPS : 33.33ms*/
#define CRTC_MAX_WAIT_ONE_FRAME     34
#define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages)

/**
 * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC
 * @hw_dspp     : DSPP HW Driver context
 * @hw_lm       : LM HW Driver context
 * @hw_ctl      : CTL Path HW driver context
 * @intf_idx    : Interface idx
 * @mode        : Interface mode Active/CMD
 * @flush_mask  : Flush mask value for this commit
 */
struct sde_crtc_mixer {
	struct sde_hw_dspp  *hw_dspp;
	struct sde_hw_mixer *hw_lm;
	struct sde_hw_ctl   *hw_ctl;
	enum sde_intf       intf_idx;
	enum sde_intf_mode  mode;
	u32 flush_mask;
};

/**
 * struct sde_crtc - virtualized CRTC data structure
 * @base          : Base drm crtc structure
 * @name          : ASCII description of this crtc
 * @encoder       : Associated drm encoder object
 * @id            : Unique crtc identifier
 * @lm_lock       : LM register access spinlock
 * @num_ctls      : Number of ctl paths in use
 * @num_mixers    : Number of mixers in use
 * @mixer         : List of active mixers
 * @event         : Pointer to last received drm vblank event
 * @pending       : Whether or not an update is pending
 * @vsync_count   : Running count of received vsync events
 */
struct sde_crtc {
	struct drm_crtc base;
	char name[8];
	struct drm_encoder *encoder;
	int id;

	spinlock_t lm_lock;	/* protect registers */

	/* HW Resources reserved for the crtc */
	u32  num_ctls;
	u32  num_mixers;
	struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS];

	/*if there is a pending flip, these will be non-null */
	struct drm_pending_vblank_event *event;
	atomic_t pending;
	u32 vsync_count;
};

#define to_sde_crtc(x) container_of(x, struct sde_crtc, base)

#endif /* _SDE_CRTC_H_ */
+54 −8
Original line number Diff line number Diff line
@@ -201,6 +201,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
{
	struct sde_encoder_virt *sde_enc = NULL;
	int i = 0;
	bool splitmode = false;

	DBG("");

@@ -211,11 +212,23 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,

	sde_enc = to_sde_encoder_virt(drm_enc);

	/*
	 * Panel is driven by two interfaces ,each interface drives half of
	 * the horizontal
	 */
	if (sde_enc->num_phys_encs == 2)
		splitmode = true;

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

		if (phys && phys->phys_ops.mode_set)
			phys->phys_ops.mode_set(phys, mode, adjusted_mode);
		if (phys) {
			phys->phys_ops.mode_set(phys,
					mode,
					adjusted_mode,
					splitmode);
			if (memcmp(mode, adjusted_mode, sizeof(*mode)) != 0)
				DRM_ERROR("adjusted modes not supported\n");
		}
	}
}

@@ -223,6 +236,7 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
{
	struct sde_encoder_virt *sde_enc = NULL;
	int i = 0;
	bool splitmode = false;

	DBG("");

@@ -235,10 +249,19 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)

	bs_set(sde_enc, 1);

	if (sde_enc->num_phys_encs == 2)
		splitmode = true;


	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

		if (phys && phys->phys_ops.enable)

			/* enable/disable dual interface top config */
			if (phys->phys_ops.enable_split_config)
				phys->phys_ops.enable_split_config(phys,
						splitmode);
			phys->phys_ops.enable(phys);
	}
}
@@ -380,13 +403,11 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
		 * h_tile_instance_ids[2] = {0, 1}; DSI0 = left, DSI1 = right
		 * h_tile_instance_ids[2] = {1, 0}; DSI1 = left, DSI0 = right
		 */
		const struct sde_hw_res_map *hw_res_map = NULL;
		enum sde_intf intf_idx = INTF_MAX;
		enum sde_ctl ctl_idx = CTL_0;
		enum sde_ctl ctl_idx = CTL_MAX;
		u32 controller_id = disp_info->h_tile_instance[i];

		if (intf_type == INTF_HDMI)
			ctl_idx = CTL_2;

		DBG("h_tile_instance %d = %d", i, controller_id);

		intf_idx = sde_encoder_get_intf(sde_kms->catalog,
@@ -396,6 +417,12 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
			ret = -EINVAL;
		}

		hw_res_map = sde_rm_get_res_map(sde_kms, intf_idx);
		if (IS_ERR_OR_NULL(hw_res_map))
			ret = -EINVAL;
		else
			ctl_idx = hw_res_map->ctl;

		/* Create both VID and CMD Phys Encoders here */
		if (!ret)
			ret = sde_encoder_virt_add_phys_vid_enc(
@@ -461,6 +488,25 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
	spin_unlock_irqrestore(&sde_enc->spin_lock, lock_flags);
}

void  sde_encoder_get_vsync_info(struct drm_encoder *drm_enc,
		struct vsync_info *vsync)
{
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
	struct sde_encoder_phys *phys;

	DBG("");

	if (!vsync) {
		DRM_ERROR("Invalid pointer");
		return;
	}

	/* we get the vsync info from the intf at index 0: master index */
	phys = sde_enc->phys_encs[0];
	if (phys)
		phys->phys_ops.get_vsync_info(phys, vsync);
}

/* encoders init,
 * initialize encoder based on displays
 */
+6 −1
Original line number Diff line number Diff line
@@ -30,7 +30,8 @@ struct sde_encoder_virt_ops {
struct sde_encoder_phys_ops {
	void (*mode_set)(struct sde_encoder_phys *encoder,
			struct drm_display_mode *mode,
			struct drm_display_mode *adjusted_mode);
			struct drm_display_mode *adjusted_mode,
			bool splitmode);
	bool (*mode_fixup)(struct sde_encoder_phys *encoder,
			const struct drm_display_mode *mode,
			struct drm_display_mode *adjusted_mode);
@@ -39,6 +40,10 @@ struct sde_encoder_phys_ops {
	void (*destroy)(struct sde_encoder_phys *encoder);
	void (*get_hw_resources)(struct sde_encoder_phys *encoder,
			struct sde_encoder_hw_resources *hw_res);
	void (*get_vsync_info)(struct sde_encoder_phys *enc,
			struct vsync_info *vsync);
	void (*enable_split_config)(struct sde_encoder_phys *enc,
			bool enable);
};

struct sde_encoder_phys {
Loading