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

Commit 97643d75 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

Latest updates on Mali DP, adding support for colour management,
plane scaling and power management.

(these have been in -next for a while).

* 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld:
  drm: mali-dp: use div_u64 for expensive 64-bit divisions
  drm: mali-dp: Check the mclk rate and allow up/down scaling
  drm: mali-dp: Enable image enhancement when scaling
  drm: mali-dp: Add plane upscaling support
  drm/mali-dp: Add core_id file to the sysfs interface
  drm: mali-dp: Add CTM support
  drm: mali-dp: enable gamma support
  drm: mali-dp: add malidp_crtc_state struct
  drm: mali-dp: add custom reset hook for planes
  drm: mali-dp: remove unused variable
  drm: mali-dp: add atomic_print_state for planes
  drm: mali-dp: Enable power management for the device.
  drm: mali-dp: Update the state of all planes before re-enabling active CRTCs.
parents 6b146270 763656d3
Loading
Loading
Loading
Loading
+325 −16
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <video/videomode.h>

#include "malidp_drv.h"
@@ -35,13 +36,6 @@ static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
	long rate, req_rate = mode->crtc_clock * 1000;

	if (req_rate) {
		rate = clk_round_rate(hwdev->mclk, req_rate);
		if (rate < req_rate) {
			DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
					 mode->crtc_clock);
			return false;
		}

		rate = clk_round_rate(hwdev->pxlclk, req_rate);
		if (rate != req_rate) {
			DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
@@ -58,9 +52,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc)
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	struct videomode vm;
	int err = pm_runtime_get_sync(crtc->dev->dev);

	drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
	if (err < 0) {
		DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
		return;
	}

	drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
	clk_prepare_enable(hwdev->pxlclk);

	/* We rely on firmware to set mclk to a sensible level. */
@@ -75,10 +74,254 @@ static void malidp_crtc_disable(struct drm_crtc *crtc)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	int err;

	drm_crtc_vblank_off(crtc);
	hwdev->enter_config_mode(hwdev);
	clk_disable_unprepare(hwdev->pxlclk);

	err = pm_runtime_put(crtc->dev->dev);
	if (err < 0) {
		DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err);
	}
}

static const struct gamma_curve_segment {
	u16 start;
	u16 end;
} segments[MALIDP_COEFFTAB_NUM_COEFFS] = {
	/* sector 0 */
	{    0,    0 }, {    1,    1 }, {    2,    2 }, {    3,    3 },
	{    4,    4 }, {    5,    5 }, {    6,    6 }, {    7,    7 },
	{    8,    8 }, {    9,    9 }, {   10,   10 }, {   11,   11 },
	{   12,   12 }, {   13,   13 }, {   14,   14 }, {   15,   15 },
	/* sector 1 */
	{   16,   19 }, {   20,   23 }, {   24,   27 }, {   28,   31 },
	/* sector 2 */
	{   32,   39 }, {   40,   47 }, {   48,   55 }, {   56,   63 },
	/* sector 3 */
	{   64,   79 }, {   80,   95 }, {   96,  111 }, {  112,  127 },
	/* sector 4 */
	{  128,  159 }, {  160,  191 }, {  192,  223 }, {  224,  255 },
	/* sector 5 */
	{  256,  319 }, {  320,  383 }, {  384,  447 }, {  448,  511 },
	/* sector 6 */
	{  512,  639 }, {  640,  767 }, {  768,  895 }, {  896, 1023 },
	{ 1024, 1151 }, { 1152, 1279 }, { 1280, 1407 }, { 1408, 1535 },
	{ 1536, 1663 }, { 1664, 1791 }, { 1792, 1919 }, { 1920, 2047 },
	{ 2048, 2175 }, { 2176, 2303 }, { 2304, 2431 }, { 2432, 2559 },
	{ 2560, 2687 }, { 2688, 2815 }, { 2816, 2943 }, { 2944, 3071 },
	{ 3072, 3199 }, { 3200, 3327 }, { 3328, 3455 }, { 3456, 3583 },
	{ 3584, 3711 }, { 3712, 3839 }, { 3840, 3967 }, { 3968, 4095 },
};

#define DE_COEFTAB_DATA(a, b) ((((a) & 0xfff) << 16) | (((b) & 0xfff)))

static void malidp_generate_gamma_table(struct drm_property_blob *lut_blob,
					u32 coeffs[MALIDP_COEFFTAB_NUM_COEFFS])
{
	struct drm_color_lut *lut = (struct drm_color_lut *)lut_blob->data;
	int i;

	for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) {
		u32 a, b, delta_in, out_start, out_end;

		delta_in = segments[i].end - segments[i].start;
		/* DP has 12-bit internal precision for its LUTs. */
		out_start = drm_color_lut_extract(lut[segments[i].start].green,
						  12);
		out_end = drm_color_lut_extract(lut[segments[i].end].green, 12);
		a = (delta_in == 0) ? 0 : ((out_end - out_start) * 256) / delta_in;
		b = out_start;
		coeffs[i] = DE_COEFTAB_DATA(a, b);
	}
}

/*
 * Check if there is a new gamma LUT and if it is of an acceptable size. Also,
 * reject any LUTs that use distinct red, green, and blue curves.
 */
static int malidp_crtc_atomic_check_gamma(struct drm_crtc *crtc,
					  struct drm_crtc_state *state)
{
	struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
	struct drm_color_lut *lut;
	size_t lut_size;
	int i;

	if (!state->color_mgmt_changed || !state->gamma_lut)
		return 0;

	if (crtc->state->gamma_lut &&
	    (crtc->state->gamma_lut->base.id == state->gamma_lut->base.id))
		return 0;

	if (state->gamma_lut->length % sizeof(struct drm_color_lut))
		return -EINVAL;

	lut_size = state->gamma_lut->length / sizeof(struct drm_color_lut);
	if (lut_size != MALIDP_GAMMA_LUT_SIZE)
		return -EINVAL;

	lut = (struct drm_color_lut *)state->gamma_lut->data;
	for (i = 0; i < lut_size; ++i)
		if (!((lut[i].red == lut[i].green) &&
		      (lut[i].red == lut[i].blue)))
			return -EINVAL;

	if (!state->mode_changed) {
		int ret;

		state->mode_changed = true;
		/*
		 * Kerneldoc for drm_atomic_helper_check_modeset mandates that
		 * it be invoked when the driver sets ->mode_changed. Since
		 * changing the gamma LUT doesn't depend on any external
		 * resources, it is safe to call it only once.
		 */
		ret = drm_atomic_helper_check_modeset(crtc->dev, state->state);
		if (ret)
			return ret;
	}

	malidp_generate_gamma_table(state->gamma_lut, mc->gamma_coeffs);
	return 0;
}

/*
 * Check if there is a new CTM and if it contains valid input. Valid here means
 * that the number is inside the representable range for a Q3.12 number,
 * excluding truncating the fractional part of the input data.
 *
 * The COLORADJ registers can be changed atomically.
 */
static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
					struct drm_crtc_state *state)
{
	struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
	struct drm_color_ctm *ctm;
	int i;

	if (!state->color_mgmt_changed)
		return 0;

	if (!state->ctm)
		return 0;

	if (crtc->state->ctm && (crtc->state->ctm->base.id ==
				 state->ctm->base.id))
		return 0;

	/*
	 * The size of the ctm is checked in
	 * drm_atomic_replace_property_blob_from_id.
	 */
	ctm = (struct drm_color_ctm *)state->ctm->data;
	for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
		/* Convert from S31.32 to Q3.12. */
		s64 val = ctm->matrix[i];
		u32 mag = ((((u64)val) & ~BIT_ULL(63)) >> 20) &
			  GENMASK_ULL(14, 0);

		/*
		 * Convert to 2s complement and check the destination's top bit
		 * for overflow. NB: Can't check before converting or it'd
		 * incorrectly reject the case:
		 * sign == 1
		 * mag == 0x2000
		 */
		if (val & BIT_ULL(63))
			mag = ~mag + 1;
		if (!!(val & BIT_ULL(63)) != !!(mag & BIT(14)))
			return -EINVAL;
		mc->coloradj_coeffs[i] = mag;
	}

	return 0;
}

static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
					    struct drm_crtc_state *state)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
	struct malidp_se_config *s = &cs->scaler_config;
	struct drm_plane *plane;
	struct videomode vm;
	const struct drm_plane_state *pstate;
	u32 h_upscale_factor = 0; /* U16.16 */
	u32 v_upscale_factor = 0; /* U16.16 */
	u8 scaling = cs->scaled_planes_mask;
	int ret;

	if (!scaling) {
		s->scale_enable = false;
		goto mclk_calc;
	}

	/* The scaling engine can only handle one plane at a time. */
	if (scaling & (scaling - 1))
		return -EINVAL;

	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		struct malidp_plane *mp = to_malidp_plane(plane);
		u32 phase;

		if (!(mp->layer->id & scaling))
			continue;

		/*
		 * Convert crtc_[w|h] to U32.32, then divide by U16.16 src_[w|h]
		 * to get the U16.16 result.
		 */
		h_upscale_factor = div_u64((u64)pstate->crtc_w << 32,
					   pstate->src_w);
		v_upscale_factor = div_u64((u64)pstate->crtc_h << 32,
					   pstate->src_h);

		s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
				      (v_upscale_factor >> 16) >= 2);

		s->input_w = pstate->src_w >> 16;
		s->input_h = pstate->src_h >> 16;
		s->output_w = pstate->crtc_w;
		s->output_h = pstate->crtc_h;

#define SE_N_PHASE 4
#define SE_SHIFT_N_PHASE 12
		/* Calculate initial_phase and delta_phase for horizontal. */
		phase = s->input_w;
		s->h_init_phase =
				((phase << SE_N_PHASE) / s->output_w + 1) / 2;

		phase = s->input_w;
		phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
		s->h_delta_phase = phase / s->output_w;

		/* Same for vertical. */
		phase = s->input_h;
		s->v_init_phase =
				((phase << SE_N_PHASE) / s->output_h + 1) / 2;

		phase = s->input_h;
		phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
		s->v_delta_phase = phase / s->output_h;
#undef SE_N_PHASE
#undef SE_SHIFT_N_PHASE
		s->plane_src_id = mp->layer->id;
	}

	s->scale_enable = true;
	s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
	s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);

mclk_calc:
	drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
	ret = hwdev->se_calc_mclk(hwdev, s, &vm);
	if (ret < 0)
		return -EINVAL;
	return 0;
}

static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
@@ -90,6 +333,7 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
	const struct drm_plane_state *pstate;
	u32 rot_mem_free, rot_mem_usable;
	int rotated_planes = 0;
	int ret;

	/*
	 * check if there is enough rotation memory available for planes
@@ -156,7 +400,11 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
		}
	}

	return 0;
	ret = malidp_crtc_atomic_check_gamma(crtc, state);
	ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
	ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);

	return ret;
}

static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
@@ -166,6 +414,60 @@ static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
	.atomic_check = malidp_crtc_atomic_check,
};

static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
{
	struct malidp_crtc_state *state, *old_state;

	if (WARN_ON(!crtc->state))
		return NULL;

	old_state = to_malidp_crtc_state(crtc->state);
	state = kmalloc(sizeof(*state), GFP_KERNEL);
	if (!state)
		return NULL;

	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
	memcpy(state->gamma_coeffs, old_state->gamma_coeffs,
	       sizeof(state->gamma_coeffs));
	memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs,
	       sizeof(state->coloradj_coeffs));
	memcpy(&state->scaler_config, &old_state->scaler_config,
	       sizeof(state->scaler_config));
	state->scaled_planes_mask = 0;

	return &state->base;
}

static void malidp_crtc_reset(struct drm_crtc *crtc)
{
	struct malidp_crtc_state *state = NULL;

	if (crtc->state) {
		state = to_malidp_crtc_state(crtc->state);
		__drm_atomic_helper_crtc_destroy_state(crtc->state);
	}

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

static void malidp_crtc_destroy_state(struct drm_crtc *crtc,
				      struct drm_crtc_state *state)
{
	struct malidp_crtc_state *mali_state = NULL;

	if (state) {
		mali_state = to_malidp_crtc_state(state);
		__drm_atomic_helper_crtc_destroy_state(state);
	}

	kfree(mali_state);
}

static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
@@ -186,12 +488,13 @@ static void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
}

static const struct drm_crtc_funcs malidp_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 = drm_atomic_helper_crtc_reset,
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
	.reset = malidp_crtc_reset,
	.atomic_duplicate_state = malidp_crtc_duplicate_state,
	.atomic_destroy_state = malidp_crtc_destroy_state,
	.enable_vblank = malidp_crtc_enable_vblank,
	.disable_vblank = malidp_crtc_disable_vblank,
};
@@ -223,11 +526,17 @@ int malidp_crtc_init(struct drm_device *drm)

	ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
					&malidp_crtc_funcs, NULL);
	if (ret)
		goto crtc_cleanup_planes;

	if (!ret) {
	drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
	drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE);
	/* No inverse-gamma: it is per-plane. */
	drm_crtc_enable_color_mgmt(&malidp->crtc, 0, true, MALIDP_GAMMA_LUT_SIZE);

	malidp_se_set_enh_coeffs(malidp->dev);

	return 0;
	}

crtc_cleanup_planes:
	malidp_de_planes_destroy(drm);
+282 −25
Original line number Diff line number Diff line
@@ -13,9 +13,11 @@
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/console.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>

#include <drm/drmP.h>
#include <drm/drm_atomic.h>
@@ -32,6 +34,131 @@

#define MALIDP_CONF_VALID_TIMEOUT	250

static void malidp_write_gamma_table(struct malidp_hw_device *hwdev,
				     u32 data[MALIDP_COEFFTAB_NUM_COEFFS])
{
	int i;
	/* Update all channels with a single gamma curve. */
	const u32 gamma_write_mask = GENMASK(18, 16);
	/*
	 * Always write an entire table, so the address field in
	 * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask
	 * directly.
	 */
	malidp_hw_write(hwdev, gamma_write_mask,
			hwdev->map.coeffs_base + MALIDP_COEF_TABLE_ADDR);
	for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i)
		malidp_hw_write(hwdev, data[i],
				hwdev->map.coeffs_base +
				MALIDP_COEF_TABLE_DATA);
}

static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc,
					      struct drm_crtc_state *old_state)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;

	if (!crtc->state->color_mgmt_changed)
		return;

	if (!crtc->state->gamma_lut) {
		malidp_hw_clearbits(hwdev,
				    MALIDP_DISP_FUNC_GAMMA,
				    MALIDP_DE_DISPLAY_FUNC);
	} else {
		struct malidp_crtc_state *mc =
			to_malidp_crtc_state(crtc->state);

		if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id !=
					      old_state->gamma_lut->base.id))
			malidp_write_gamma_table(hwdev, mc->gamma_coeffs);

		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA,
				  MALIDP_DE_DISPLAY_FUNC);
	}
}

static
void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc,
					  struct drm_crtc_state *old_state)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	int i;

	if (!crtc->state->color_mgmt_changed)
		return;

	if (!crtc->state->ctm) {
		malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ,
				    MALIDP_DE_DISPLAY_FUNC);
	} else {
		struct malidp_crtc_state *mc =
			to_malidp_crtc_state(crtc->state);

		if (!old_state->ctm || (crtc->state->ctm->base.id !=
					old_state->ctm->base.id))
			for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i)
				malidp_hw_write(hwdev,
						mc->coloradj_coeffs[i],
						hwdev->map.coeffs_base +
						MALIDP_COLOR_ADJ_COEF + 4 * i);

		malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ,
				  MALIDP_DE_DISPLAY_FUNC);
	}
}

static void malidp_atomic_commit_se_config(struct drm_crtc *crtc,
					   struct drm_crtc_state *old_state)
{
	struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state);
	struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state);
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	struct malidp_se_config *s = &cs->scaler_config;
	struct malidp_se_config *old_s = &old_cs->scaler_config;
	u32 se_control = hwdev->map.se_base +
			 ((hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
			 0x10 : 0xC);
	u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL;
	u32 scr = se_control + MALIDP_SE_SCALING_CONTROL;
	u32 val;

	/* Set SE_CONTROL */
	if (!s->scale_enable) {
		val = malidp_hw_read(hwdev, se_control);
		val &= ~MALIDP_SE_SCALING_EN;
		malidp_hw_write(hwdev, val, se_control);
		return;
	}

	hwdev->se_set_scaling_coeffs(hwdev, s, old_s);
	val = malidp_hw_read(hwdev, se_control);
	val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN;

	val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK);
	val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0;

	val |= MALIDP_SE_RGBO_IF_EN;
	malidp_hw_write(hwdev, val, se_control);

	/* Set IN_SIZE & OUT_SIZE. */
	val = MALIDP_SE_SET_V_SIZE(s->input_h) |
	      MALIDP_SE_SET_H_SIZE(s->input_w);
	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE);
	val = MALIDP_SE_SET_V_SIZE(s->output_h) |
	      MALIDP_SE_SET_H_SIZE(s->output_w);
	malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE);

	/* Set phase regs. */
	malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH);
	malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH);
	malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH);
	malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH);
}

/*
 * set the "config valid" bit and wait until the hardware acts on it
 */
@@ -66,10 +193,12 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
	struct drm_pending_vblank_event *event;
	struct drm_device *drm = state->dev;
	struct malidp_drm *malidp = drm->dev_private;
	int ret = malidp_set_and_wait_config_valid(drm);

	if (ret)
	if (malidp->crtc.enabled) {
		/* only set config_valid if the CRTC is enabled */
		if (malidp_set_and_wait_config_valid(drm))
			DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
	}

	event = malidp->crtc.state->event;
	if (event) {
@@ -88,15 +217,30 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
{
	struct drm_device *drm = state->dev;
	struct drm_crtc *crtc;
	struct drm_crtc_state *old_crtc_state;
	int i;

	pm_runtime_get_sync(drm->dev);

	drm_atomic_helper_commit_modeset_disables(drm, state);
	drm_atomic_helper_commit_modeset_enables(drm, state);

	for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
		malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
		malidp_atomic_commit_update_coloradj(crtc, old_crtc_state);
		malidp_atomic_commit_se_config(crtc, old_crtc_state);
	}

	drm_atomic_helper_commit_planes(drm, state, 0);

	drm_atomic_helper_commit_modeset_enables(drm, state);

	malidp_atomic_commit_hw_done(state);

	drm_atomic_helper_wait_for_vblanks(drm, state);

	pm_runtime_put(drm->dev);

	drm_atomic_helper_cleanup_planes(drm, state);
}

@@ -277,8 +421,65 @@ static bool malidp_has_sufficient_address_space(const struct resource *res,
	return true;
}

static ssize_t core_id_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;

	return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id);
}

DEVICE_ATTR_RO(core_id);

static int malidp_init_sysfs(struct device *dev)
{
	int ret = device_create_file(dev, &dev_attr_core_id);

	if (ret)
		DRM_ERROR("failed to create device file for core_id\n");

	return ret;
}

static void malidp_fini_sysfs(struct device *dev)
{
	device_remove_file(dev, &dev_attr_core_id);
}

#define MAX_OUTPUT_CHANNELS	3

static int malidp_runtime_pm_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	/* we can only suspend if the hardware is in config mode */
	WARN_ON(!hwdev->in_config_mode(hwdev));

	hwdev->pm_suspended = true;
	clk_disable_unprepare(hwdev->mclk);
	clk_disable_unprepare(hwdev->aclk);
	clk_disable_unprepare(hwdev->pclk);

	return 0;
}

static int malidp_runtime_pm_resume(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	clk_prepare_enable(hwdev->pclk);
	clk_prepare_enable(hwdev->aclk);
	clk_prepare_enable(hwdev->mclk);
	hwdev->pm_suspended = false;

	return 0;
}

static int malidp_bind(struct device *dev)
{
	struct resource *res;
@@ -307,7 +508,6 @@ static int malidp_bind(struct device *dev)
	memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
	malidp->dev = hwdev;


	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	hwdev->regs = devm_ioremap_resource(dev, res);
	if (IS_ERR(hwdev->regs))
@@ -340,14 +540,17 @@ static int malidp_bind(struct device *dev)
		goto alloc_fail;
	}

	/* Enable APB clock in order to get access to the registers */
	clk_prepare_enable(hwdev->pclk);
	/*
	 * Enable AXI clock and main clock so that prefetch can start once
	 * the registers are set
	 */
	clk_prepare_enable(hwdev->aclk);
	clk_prepare_enable(hwdev->mclk);
	drm->dev_private = malidp;
	dev_set_drvdata(dev, drm);

	/* Enable power management */
	pm_runtime_enable(dev);

	/* Resume device to enable the clocks */
	if (pm_runtime_enabled(dev))
		pm_runtime_get_sync(dev);
	else
		malidp_runtime_pm_resume(dev);

	dev_id = of_match_device(malidp_drm_of_match, dev);
	if (!dev_id) {
@@ -376,6 +579,8 @@ static int malidp_bind(struct device *dev)
	DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
		 (version >> 12) & 0xf, (version >> 8) & 0xf);

	malidp->core_id = version;

	/* set the number of lines used for output of RGB data */
	ret = of_property_read_u8_array(dev->of_node,
					"arm,malidp-output-port-lines",
@@ -387,13 +592,15 @@ static int malidp_bind(struct device *dev)
		out_depth = (out_depth << 8) | (output_width[i] & 0xf);
	malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);

	drm->dev_private = malidp;
	dev_set_drvdata(dev, drm);
	atomic_set(&malidp->config_valid, 0);
	init_waitqueue_head(&malidp->wq);

	ret = malidp_init(drm);
	if (ret < 0)
		goto query_hw_fail;

	ret = malidp_init_sysfs(dev);
	if (ret)
		goto init_fail;

	/* Set the CRTC's port so that the encoder component can find it */
@@ -416,6 +623,7 @@ static int malidp_bind(struct device *dev)
		DRM_ERROR("failed to initialise vblank\n");
		goto vblank_fail;
	}
	pm_runtime_put(dev);

	drm_mode_config_reset(drm);

@@ -441,7 +649,9 @@ static int malidp_bind(struct device *dev)
		drm_fbdev_cma_fini(malidp->fbdev);
		malidp->fbdev = NULL;
	}
	drm_kms_helper_poll_fini(drm);
fbdev_fail:
	pm_runtime_get_sync(dev);
	drm_vblank_cleanup(drm);
vblank_fail:
	malidp_se_irq_fini(drm);
@@ -452,14 +662,17 @@ static int malidp_bind(struct device *dev)
bind_fail:
	of_node_put(malidp->crtc.port);
	malidp->crtc.port = NULL;
	malidp_fini(drm);
init_fail:
	malidp_fini_sysfs(dev);
	malidp_fini(drm);
query_hw_fail:
	pm_runtime_put(dev);
	if (pm_runtime_enabled(dev))
		pm_runtime_disable(dev);
	else
		malidp_runtime_pm_suspend(dev);
	drm->dev_private = NULL;
	dev_set_drvdata(dev, NULL);
query_hw_fail:
	clk_disable_unprepare(hwdev->mclk);
	clk_disable_unprepare(hwdev->aclk);
	clk_disable_unprepare(hwdev->pclk);
	drm_dev_unref(drm);
alloc_fail:
	of_reserved_mem_device_release(dev);
@@ -471,7 +684,6 @@ static void malidp_unbind(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	drm_dev_unregister(drm);
	if (malidp->fbdev) {
@@ -479,18 +691,22 @@ static void malidp_unbind(struct device *dev)
		malidp->fbdev = NULL;
	}
	drm_kms_helper_poll_fini(drm);
	pm_runtime_get_sync(dev);
	drm_vblank_cleanup(drm);
	malidp_se_irq_fini(drm);
	malidp_de_irq_fini(drm);
	drm_vblank_cleanup(drm);
	component_unbind_all(dev, drm);
	of_node_put(malidp->crtc.port);
	malidp->crtc.port = NULL;
	malidp_fini_sysfs(dev);
	malidp_fini(drm);
	pm_runtime_put(dev);
	if (pm_runtime_enabled(dev))
		pm_runtime_disable(dev);
	else
		malidp_runtime_pm_suspend(dev);
	drm->dev_private = NULL;
	dev_set_drvdata(dev, NULL);
	clk_disable_unprepare(hwdev->mclk);
	clk_disable_unprepare(hwdev->aclk);
	clk_disable_unprepare(hwdev->pclk);
	drm_dev_unref(drm);
	of_reserved_mem_device_release(dev);
}
@@ -533,11 +749,52 @@ static int malidp_platform_remove(struct platform_device *pdev)
	return 0;
}

static int __maybe_unused malidp_pm_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;

	drm_kms_helper_poll_disable(drm);
	console_lock();
	drm_fbdev_cma_set_suspend(malidp->fbdev, 1);
	console_unlock();
	malidp->pm_state = drm_atomic_helper_suspend(drm);
	if (IS_ERR(malidp->pm_state)) {
		console_lock();
		drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
		console_unlock();
		drm_kms_helper_poll_enable(drm);
		return PTR_ERR(malidp->pm_state);
	}

	return 0;
}

static int __maybe_unused malidp_pm_resume(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;

	drm_atomic_helper_resume(drm, malidp->pm_state);
	console_lock();
	drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
	console_unlock();
	drm_kms_helper_poll_enable(drm);

	return 0;
}

static const struct dev_pm_ops malidp_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
	SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
};

static struct platform_driver malidp_platform_driver = {
	.probe		= malidp_platform_probe,
	.remove		= malidp_platform_remove,
	.driver	= {
		.name = "mali-dp",
		.pm = &malidp_pm_ops,
		.of_match_table	= malidp_drm_of_match,
	},
};
+13 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ struct malidp_drm {
	struct drm_crtc crtc;
	wait_queue_head_t wq;
	atomic_t config_valid;
	struct drm_atomic_state *pm_state;
	u32 core_id;
};

#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
@@ -47,6 +49,17 @@ struct malidp_plane_state {
#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)

struct malidp_crtc_state {
	struct drm_crtc_state base;
	u32 gamma_coeffs[MALIDP_COEFFTAB_NUM_COEFFS];
	u32 coloradj_coeffs[MALIDP_COLORADJ_NUM_COEFFS];
	struct malidp_se_config scaler_config;
	/* Bitfield of all the planes that have requested a scaled output. */
	u8 scaled_planes_mask;
};

#define to_malidp_crtc_state(x) container_of(x, struct malidp_crtc_state, base)

int malidp_de_planes_init(struct drm_device *drm);
void malidp_de_planes_destroy(struct drm_device *drm);
int malidp_crtc_init(struct drm_device *drm);
+213 −0

File changed.

Preview size limit exceeded, changes collapsed.

+81 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading