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

Commit 96698897 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents f8278058 e14b3005
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