Commit f2e8d169 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next



This is the 2nd pull request for the malidp-next. The new patches add
additional support for Arm Mali D71 so that it can now be enabled
correctly and brought up on any SoC that contains the IP. From now on
we will start focusing on adding writeback, scaling and other useful
features to bring the driver to the same level of maturity as mali-dp.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Liviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190507103712.GJ15144@e110455-lin.cambridge.arm.com
parents 30d62d44 15e9122d
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -20,4 +20,16 @@
/* Mali-display product IDs */
#define MALIDP_D71_PRODUCT_ID   0x0071

union komeda_config_id {
	struct {
		__u32	max_line_sz:16,
			n_pipelines:2,
			n_scalers:2, /* number of scalers per pipeline */
			n_layers:3, /* number of layers per pipeline */
			n_richs:3, /* number of rich layers per pipeline */
			reserved_bits:6;
	};
	__u32 value;
};

#endif /* _MALIDP_PRODUCT_H_ */
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ komeda-y := \
	komeda_dev.o \
	komeda_format_caps.o \
	komeda_pipeline.o \
	komeda_pipeline_state.o \
	komeda_framebuffer.o \
	komeda_kms.o \
	komeda_crtc.o \
+5 −4
Original line number Diff line number Diff line
@@ -391,7 +391,7 @@ static void d71_compiz_dump(struct komeda_component *c, struct seq_file *sf)
	seq_printf(sf, "CU_USER_HIGH:\t\t0x%X\n", v[1]);
}

struct komeda_component_funcs d71_compiz_funcs = {
static struct komeda_component_funcs d71_compiz_funcs = {
	.update		= d71_compiz_update,
	.disable	= d71_component_disable,
	.dump_register	= d71_compiz_dump,
@@ -467,7 +467,7 @@ static void d71_improc_dump(struct komeda_component *c, struct seq_file *sf)
		seq_printf(sf, "IPS_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
}

struct komeda_component_funcs d71_improc_funcs = {
static struct komeda_component_funcs d71_improc_funcs = {
	.update		= d71_improc_update,
	.disable	= d71_component_disable,
	.dump_register	= d71_improc_dump,
@@ -543,7 +543,8 @@ static void d71_timing_ctrlr_update(struct komeda_component *c,
	malidp_write32(reg, BLK_CONTROL, value);
}

void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf)
static void d71_timing_ctrlr_dump(struct komeda_component *c,
				  struct seq_file *sf)
{
	u32 v[8], i;

@@ -579,7 +580,7 @@ void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf)
	seq_printf(sf, "BS_USER:\t\t0x%X\n", v[4]);
}

struct komeda_component_funcs d71_timing_ctrlr_funcs = {
static struct komeda_component_funcs d71_timing_ctrlr_funcs = {
	.update		= d71_timing_ctrlr_update,
	.disable	= d71_timing_ctrlr_disable,
	.dump_register	= d71_timing_ctrlr_dump,
+54 −0
Original line number Diff line number Diff line
@@ -243,6 +243,56 @@ static int d71_disable_irq(struct komeda_dev *mdev)
	return 0;
}

static void d71_on_off_vblank(struct komeda_dev *mdev, int master_pipe, bool on)
{
	struct d71_dev *d71 = mdev->chip_data;
	struct d71_pipeline *pipe = d71->pipes[master_pipe];

	malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK,
			    DOU_IRQ_PL0, on ? DOU_IRQ_PL0 : 0);
}

static int to_d71_opmode(int core_mode)
{
	switch (core_mode) {
	case KOMEDA_MODE_DISP0:
		return DO0_ACTIVE_MODE;
	case KOMEDA_MODE_DISP1:
		return DO1_ACTIVE_MODE;
	case KOMEDA_MODE_DUAL_DISP:
		return DO01_ACTIVE_MODE;
	case KOMEDA_MODE_INACTIVE:
		return INACTIVE_MODE;
	default:
		WARN(1, "Unknown operation mode");
		return INACTIVE_MODE;
	}
}

static int d71_change_opmode(struct komeda_dev *mdev, int new_mode)
{
	struct d71_dev *d71 = mdev->chip_data;
	u32 opmode = to_d71_opmode(new_mode);
	int ret;

	malidp_write32_mask(d71->gcu_addr, BLK_CONTROL, 0x7, opmode);

	ret = dp_wait_cond(((malidp_read32(d71->gcu_addr, BLK_CONTROL) & 0x7) == opmode),
			   100, 1000, 10000);

	return ret > 0 ? 0 : -ETIMEDOUT;
}

static void d71_flush(struct komeda_dev *mdev,
		      int master_pipe, u32 active_pipes)
{
	struct d71_dev *d71 = mdev->chip_data;
	u32 reg_offset = (master_pipe == 0) ?
			 GCU_CONFIG_VALID0 : GCU_CONFIG_VALID1;

	malidp_write32(d71->gcu_addr, reg_offset, GCU_CONFIG_CVAL);
}

static int d71_reset(struct d71_dev *d71)
{
	u32 __iomem *gcu = d71->gcu_addr;
@@ -459,6 +509,9 @@ static struct komeda_dev_funcs d71_chip_funcs = {
	.irq_handler	= d71_irq_handler,
	.enable_irq	= d71_enable_irq,
	.disable_irq	= d71_disable_irq,
	.on_off_vblank	= d71_on_off_vblank,
	.change_opmode	= d71_change_opmode,
	.flush		= d71_flush,
};

struct komeda_dev_funcs *
@@ -467,6 +520,7 @@ d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip)
	chip->arch_id	= malidp_read32(reg_base, GLB_ARCH_ID);
	chip->core_id	= malidp_read32(reg_base, GLB_CORE_ID);
	chip->core_info	= malidp_read32(reg_base, GLB_CORE_INFO);
	chip->bus_width	= D71_BUS_WIDTH_16_BYTES;

	return &d71_chip_funcs;
}
+391 −4
Original line number Diff line number Diff line
@@ -18,6 +18,144 @@
#include "komeda_dev.h"
#include "komeda_kms.h"

/**
 * komeda_crtc_atomic_check - build display output data flow
 * @crtc: DRM crtc
 * @state: the crtc state object
 *
 * crtc_atomic_check is the final check stage, so beside build a display data
 * pipeline according to the crtc_state, but still needs to release or disable
 * the unclaimed pipeline resources.
 *
 * RETURNS:
 * Zero for success or -errno
 */
static int
komeda_crtc_atomic_check(struct drm_crtc *crtc,
			 struct drm_crtc_state *state)
{
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
	int err;

	if (state->active) {
		err = komeda_build_display_data_flow(kcrtc, kcrtc_st);
		if (err)
			return err;
	}

	/* release unclaimed pipeline resources */
	err = komeda_release_unclaimed_resources(kcrtc->master, kcrtc_st);
	if (err)
		return err;

	return 0;
}

static u32 komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
{
	unsigned long mclk = kcrtc_st->base.adjusted_mode.clock * 1000;

	return mclk;
}

/* For active a crtc, mainly need two parts of preparation
 * 1. adjust display operation mode.
 * 2. enable needed clk
 */
static int
komeda_crtc_prepare(struct komeda_crtc *kcrtc)
{
	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
	struct komeda_pipeline *master = kcrtc->master;
	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state);
	unsigned long pxlclk_rate = kcrtc_st->base.adjusted_mode.clock * 1000;
	u32 new_mode;
	int err;

	mutex_lock(&mdev->lock);

	new_mode = mdev->dpmode | BIT(master->id);
	if (WARN_ON(new_mode == mdev->dpmode)) {
		err = 0;
		goto unlock;
	}

	err = mdev->funcs->change_opmode(mdev, new_mode);
	if (err) {
		DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
			  mdev->dpmode, new_mode);
		goto unlock;
	}

	mdev->dpmode = new_mode;
	/* Only need to enable mclk on single display mode, but no need to
	 * enable mclk it on dual display mode, since the dual mode always
	 * switch from single display mode, the mclk already enabled, no need
	 * to enable it again.
	 */
	if (new_mode != KOMEDA_MODE_DUAL_DISP) {
		err = clk_set_rate(mdev->mclk, komeda_calc_mclk(kcrtc_st));
		if (err)
			DRM_ERROR("failed to set mclk.\n");
		err = clk_prepare_enable(mdev->mclk);
		if (err)
			DRM_ERROR("failed to enable mclk.\n");
	}

	err = clk_prepare_enable(master->aclk);
	if (err)
		DRM_ERROR("failed to enable axi clk for pipe%d.\n", master->id);
	err = clk_set_rate(master->pxlclk, pxlclk_rate);
	if (err)
		DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id);
	err = clk_prepare_enable(master->pxlclk);
	if (err)
		DRM_ERROR("failed to enable pxl clk for pipe%d.\n", master->id);

unlock:
	mutex_unlock(&mdev->lock);

	return err;
}

static int
komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
{
	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
	struct komeda_pipeline *master = kcrtc->master;
	u32 new_mode;
	int err;

	mutex_lock(&mdev->lock);

	new_mode = mdev->dpmode & (~BIT(master->id));

	if (WARN_ON(new_mode == mdev->dpmode)) {
		err = 0;
		goto unlock;
	}

	err = mdev->funcs->change_opmode(mdev, new_mode);
	if (err) {
		DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
			  mdev->dpmode, new_mode);
		goto unlock;
	}

	mdev->dpmode = new_mode;

	clk_disable_unprepare(master->pxlclk);
	clk_disable_unprepare(master->aclk);
	if (new_mode == KOMEDA_MODE_INACTIVE)
		clk_disable_unprepare(mdev->mclk);

unlock:
	mutex_unlock(&mdev->lock);

	return err;
}

void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
			      struct komeda_events *evts)
{
@@ -31,15 +169,264 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
	if (events & KOMEDA_EVENT_EOW)
		DRM_DEBUG("EOW.\n");

	/* will handle it with crtc->flush */
	if (events & KOMEDA_EVENT_FLIP)
		DRM_DEBUG("FLIP Done.\n");
	if (events & KOMEDA_EVENT_FLIP) {
		unsigned long flags;
		struct drm_pending_vblank_event *event;

		spin_lock_irqsave(&crtc->dev->event_lock, flags);
		if (kcrtc->disable_done) {
			complete_all(kcrtc->disable_done);
			kcrtc->disable_done = NULL;
		} else if (crtc->state->event) {
			event = crtc->state->event;
			/*
			 * Consume event before notifying drm core that flip
			 * happened.
			 */
			crtc->state->event = NULL;
			drm_crtc_send_vblank_event(crtc, event);
		} else {
			DRM_WARN("CRTC[%d]: FLIP happen but no pending commit.\n",
				 drm_crtc_index(&kcrtc->base));
		}
		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
	}
}

static void
komeda_crtc_do_flush(struct drm_crtc *crtc,
		     struct drm_crtc_state *old)
{
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
	struct komeda_pipeline *master = kcrtc->master;

	DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
			 drm_crtc_index(crtc),
			 kcrtc_st->active_pipes, kcrtc_st->affected_pipes);

	/* step 1: update the pipeline/component state to HW */
	if (has_bit(master->id, kcrtc_st->affected_pipes))
		komeda_pipeline_update(master, old->state);

	/* step 2: notify the HW to kickoff the update */
	mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
}

struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
static void
komeda_crtc_atomic_enable(struct drm_crtc *crtc,
			  struct drm_crtc_state *old)
{
	komeda_crtc_prepare(to_kcrtc(crtc));
	drm_crtc_vblank_on(crtc);
	komeda_crtc_do_flush(crtc, old);
}

static void
komeda_crtc_atomic_disable(struct drm_crtc *crtc,
			   struct drm_crtc_state *old)
{
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
	struct komeda_crtc_state *old_st = to_kcrtc_st(old);
	struct komeda_dev *mdev = crtc->dev->dev_private;
	struct komeda_pipeline *master = kcrtc->master;
	struct completion *disable_done = &crtc->state->commit->flip_done;
	struct completion temp;
	int timeout;

	DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x.\n",
			 drm_crtc_index(crtc),
			 old_st->active_pipes, old_st->affected_pipes);

	if (has_bit(master->id, old_st->active_pipes))
		komeda_pipeline_disable(master, old->state);

	/* crtc_disable has two scenarios according to the state->active switch.
	 * 1. active -> inactive
	 *    this commit is a disable commit. and the commit will be finished
	 *    or done after the disable operation. on this case we can directly
	 *    use the crtc->state->event to tracking the HW disable operation.
	 * 2. active -> active
	 *    the crtc->commit is not for disable, but a modeset operation when
	 *    crtc is active, such commit actually has been completed by 3
	 *    DRM operations:
	 *    crtc_disable, update_planes(crtc_flush), crtc_enable
	 *    so on this case the crtc->commit is for the whole process.
	 *    we can not use it for tracing the disable, we need a temporary
	 *    flip_done for tracing the disable. and crtc->state->event for
	 *    the crtc_enable operation.
	 *    That's also the reason why skip modeset commit in
	 *    komeda_crtc_atomic_flush()
	 */
	if (crtc->state->active) {
		struct komeda_pipeline_state *pipe_st;
		/* clear the old active_comps to zero */
		pipe_st = komeda_pipeline_get_old_state(master, old->state);
		pipe_st->active_comps = 0;

		init_completion(&temp);
		kcrtc->disable_done = &temp;
		disable_done = &temp;
	}

	mdev->funcs->flush(mdev, master->id, 0);

	/* wait the disable take affect.*/
	timeout = wait_for_completion_timeout(disable_done, HZ);
	if (timeout == 0) {
		DRM_ERROR("disable pipeline%d timeout.\n", kcrtc->master->id);
		if (crtc->state->active) {
			unsigned long flags;

			spin_lock_irqsave(&crtc->dev->event_lock, flags);
			kcrtc->disable_done = NULL;
			spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
		}
	}

	drm_crtc_vblank_off(crtc);
	komeda_crtc_unprepare(kcrtc);
}

static void
komeda_crtc_atomic_flush(struct drm_crtc *crtc,
			 struct drm_crtc_state *old)
{
	/* commit with modeset will be handled in enable/disable */
	if (drm_atomic_crtc_needs_modeset(crtc->state))
		return;

	komeda_crtc_do_flush(crtc, old);
}

static enum drm_mode_status
komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m)
{
	struct komeda_dev *mdev = crtc->dev->dev_private;
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
	struct komeda_pipeline *master = kcrtc->master;
	long mode_clk, pxlclk;

	if (m->flags & DRM_MODE_FLAG_INTERLACE)
		return MODE_NO_INTERLACE;

	/* main clock/AXI clk must be faster than pxlclk*/
	mode_clk = m->clock * 1000;
	pxlclk = clk_round_rate(master->pxlclk, mode_clk);
	if (pxlclk != mode_clk) {
		DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk);

		return MODE_NOCLOCK;
	}

	if (clk_round_rate(mdev->mclk, mode_clk) < pxlclk) {
		DRM_DEBUG_ATOMIC("mclk can't satisfy the requirement of %s-clk: %ld.\n",
				 m->name, pxlclk);

		return MODE_CLOCK_HIGH;
	}

	if (clk_round_rate(master->aclk, mode_clk) < pxlclk) {
		DRM_DEBUG_ATOMIC("aclk can't satisfy the requirement of %s-clk: %ld.\n",
				 m->name, pxlclk);

		return MODE_CLOCK_HIGH;
	}

	return MODE_OK;
}

static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc,
				   const struct drm_display_mode *m,
				   struct drm_display_mode *adjusted_mode)
{
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
	struct komeda_pipeline *master = kcrtc->master;
	long mode_clk = m->clock * 1000;

	adjusted_mode->clock = clk_round_rate(master->pxlclk, mode_clk) / 1000;

	return true;
}

static struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
	.atomic_check	= komeda_crtc_atomic_check,
	.atomic_flush	= komeda_crtc_atomic_flush,
	.atomic_enable	= komeda_crtc_atomic_enable,
	.atomic_disable	= komeda_crtc_atomic_disable,
	.mode_valid	= komeda_crtc_mode_valid,
	.mode_fixup	= komeda_crtc_mode_fixup,
};

static void komeda_crtc_reset(struct drm_crtc *crtc)
{
	struct komeda_crtc_state *state;

	if (crtc->state)
		__drm_atomic_helper_crtc_destroy_state(crtc->state);

	kfree(to_kcrtc_st(crtc->state));
	crtc->state = NULL;

	state = kzalloc(sizeof(*state), GFP_KERNEL);
	if (state) {
		crtc->state = &state->base;
		crtc->state->crtc = crtc;
	}
}

static struct drm_crtc_state *
komeda_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
	struct komeda_crtc_state *old = to_kcrtc_st(crtc->state);
	struct komeda_crtc_state *new;

	new = kzalloc(sizeof(*new), GFP_KERNEL);
	if (!new)
		return NULL;

	__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);

	new->affected_pipes = old->active_pipes;

	return &new->base;
}

static void komeda_crtc_atomic_destroy_state(struct drm_crtc *crtc,
					     struct drm_crtc_state *state)
{
	__drm_atomic_helper_crtc_destroy_state(state);
	kfree(to_kcrtc_st(state));
}

static int komeda_crtc_vblank_enable(struct drm_crtc *crtc)
{
	struct komeda_dev *mdev = crtc->dev->dev_private;
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);

	mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, true);
	return 0;
}

static void komeda_crtc_vblank_disable(struct drm_crtc *crtc)
{
	struct komeda_dev *mdev = crtc->dev->dev_private;
	struct komeda_crtc *kcrtc = to_kcrtc(crtc);

	mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, false);
}

static const struct drm_crtc_funcs komeda_crtc_funcs = {
	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
	.destroy		= drm_crtc_cleanup,
	.set_config		= drm_atomic_helper_set_config,
	.page_flip		= drm_atomic_helper_page_flip,
	.reset			= komeda_crtc_reset,
	.atomic_duplicate_state	= komeda_crtc_atomic_duplicate_state,
	.atomic_destroy_state	= komeda_crtc_atomic_destroy_state,
	.enable_vblank		= komeda_crtc_vblank_enable,
	.disable_vblank		= komeda_crtc_vblank_disable,
};

int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
Loading