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

Commit 4174abef 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: refer to IRQ state when enable/disable AD IRQ"

parents 2450f918 da310e3d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -211,6 +211,7 @@
			qcom,sde-dspp-gamut = <0x1000 0x00040000>;
			qcom,sde-dspp-pcc = <0x1700 0x00040000>;
			qcom,sde-dspp-gc = <0x17c0 0x00010008>;
			qcom,sde-dspp-hist = <0x800 0x00010007>;
		};

		qcom,platform-supply-entries {
+365 −6
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ static void dspp_gc_install_property(struct drm_crtc *crtc);

static void dspp_igc_install_property(struct drm_crtc *crtc);

static void dspp_hist_install_property(struct drm_crtc *crtc);

typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);

static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -77,6 +79,8 @@ static void sde_cp_notify_ad_event(struct drm_crtc *crtc_drm, void *arg);
static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc,
		enum ad_property ad_prop);

static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);

#define setup_dspp_prop_install_funcs(func) \
do { \
	func[SDE_DSPP_PCC] = dspp_pcc_install_property; \
@@ -86,6 +90,7 @@ do { \
	func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
	func[SDE_DSPP_GC] = dspp_gc_install_property; \
	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
} while (0)

typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -111,7 +116,8 @@ enum {
	SDE_CP_CRTC_DSPP_SIXZONE,
	SDE_CP_CRTC_DSPP_GAMUT,
	SDE_CP_CRTC_DSPP_DITHER,
	SDE_CP_CRTC_DSPP_HIST,
	SDE_CP_CRTC_DSPP_HIST_CTRL,
	SDE_CP_CRTC_DSPP_HIST_IRQ,
	SDE_CP_CRTC_DSPP_AD,
	SDE_CP_CRTC_DSPP_VLUT,
	SDE_CP_CRTC_DSPP_AD_MODE,
@@ -365,6 +371,12 @@ void sde_cp_crtc_init(struct drm_crtc *crtc)
		return;
	}

	/* create blob to store histogram data */
	sde_crtc->hist_blob = drm_property_create_blob(crtc->dev,
				sizeof(struct drm_msm_hist), NULL);
	if (IS_ERR(sde_crtc->hist_blob))
		sde_crtc->hist_blob = NULL;

	mutex_init(&sde_crtc->crtc_cp_lock);
	INIT_LIST_HEAD(&sde_crtc->active_list);
	INIT_LIST_HEAD(&sde_crtc->dirty_list);
@@ -532,6 +544,77 @@ static void sde_cp_crtc_install_enum_property(struct drm_crtc *crtc,
	sde_cp_crtc_prop_attach(&prop_attach);
}

static struct sde_crtc_irq_info *_sde_cp_get_intr_node(u32 event,
				struct sde_crtc *sde_crtc)
{
	bool found = false;
	struct sde_crtc_irq_info *node = NULL;

	list_for_each_entry(node, &sde_crtc->user_event_list, list) {
		if (node->event == event) {
			found = true;
			break;
		}
	}

	if (!found)
		node = NULL;

	return node;
}

static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc)
{
	struct drm_crtc *crtc_drm = &sde_crtc->base;
	struct sde_kms *kms = NULL;
	struct sde_hw_mixer *hw_lm;
	struct sde_hw_dspp *hw_dspp = NULL;
	struct sde_crtc_irq_info *node = NULL;
	int i, irq_idx, ret = 0;
	unsigned long flags;

	if (!crtc_drm) {
		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
		return;
	}

	kms = get_kms(crtc_drm);

	for (i = 0; i < sde_crtc->num_mixers; i++) {
		hw_lm = sde_crtc->mixers[i].hw_lm;
		hw_dspp = sde_crtc->mixers[i].hw_dspp;
		if (!hw_lm->cfg.right_mixer)
			break;
	}

	if (!hw_dspp) {
		DRM_ERROR("invalid dspp\n");
		return;
	}

	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
					hw_dspp->idx);
	if (irq_idx < 0) {
		DRM_ERROR("failed to get irq idx\n");
		return;
	}

	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, sde_crtc);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!node)
		return;

	if (node->state == IRQ_DISABLED) {
		ret = sde_core_irq_enable(kms, &irq_idx, 1);
		if (ret)
			DRM_ERROR("failed to enable irq %d\n", irq_idx);
		else
			node->state = IRQ_ENABLED;
	}
}

static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
				   struct sde_crtc *sde_crtc)
{
@@ -640,6 +723,21 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
			}
			hw_lm->ops.setup_gc(hw_lm, &hw_cfg);
			break;
		case SDE_CP_CRTC_DSPP_HIST_CTRL:
			if (!hw_dspp || !hw_dspp->ops.setup_histogram) {
				ret = -EINVAL;
				continue;
			}
			hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled);
			break;
		case SDE_CP_CRTC_DSPP_HIST_IRQ:
			if (!hw_dspp || !hw_lm) {
				ret = -EINVAL;
				continue;
			}
			if (!hw_lm->cfg.right_mixer)
				_sde_cp_crtc_enable_hist_irq(sde_crtc);
			break;
		case SDE_CP_CRTC_DSPP_AD_MODE:
			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
				ret = -EINVAL;
@@ -1034,6 +1132,9 @@ void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc)
		kfree(prop_node);
	}

	if (sde_crtc->hist_blob)
		drm_property_unreference_blob(sde_crtc->hist_blob);

	mutex_destroy(&sde_crtc->crtc_cp_lock);
	INIT_LIST_HEAD(&sde_crtc->active_list);
	INIT_LIST_HEAD(&sde_crtc->dirty_list);
@@ -1295,6 +1396,30 @@ static void dspp_igc_install_property(struct drm_crtc *crtc)
	}
}

static void dspp_hist_install_property(struct drm_crtc *crtc)
{
	struct sde_kms *kms = NULL;
	struct sde_mdss_cfg *catalog = NULL;
	u32 version;

	kms = get_kms(crtc);
	catalog = kms->catalog;

	version = catalog->dspp[0].sblk->hist.version >> 16;
	switch (version) {
	case 1:
		sde_cp_crtc_install_enum_property(crtc,
			SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes,
			ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1");
		sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1",
			SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0);
		break;
	default:
		DRM_ERROR("version %d not supported\n", version);
		break;
	}
}

static void sde_cp_update_list(struct sde_cp_node *prop_node,
		struct sde_crtc *crtc, bool dirty_list)
{
@@ -1416,6 +1541,7 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en,
	int i;
	int irq_idx, ret;
	struct sde_cp_node prop_node;
	struct sde_crtc_irq_info *node = NULL;

	if (!crtc_drm || !ad_irq) {
		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, ad_irq);
@@ -1460,8 +1586,23 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en,
		goto exit;
	}

	node = _sde_cp_get_intr_node(DRM_EVENT_AD_BACKLIGHT, crtc);

	if (!en) {
		sde_core_irq_disable(kms, &irq_idx, 1);
		if (node) {
			if (node->state == IRQ_ENABLED) {
				ret = sde_core_irq_disable(kms, &irq_idx, 1);
				if (ret)
					DRM_ERROR("disable irq %d error %d\n",
						irq_idx, ret);
				else
					node->state = IRQ_NOINIT;
			} else {
				node->state = IRQ_NOINIT;
			}
		} else {
			DRM_ERROR("failed to get node from crtc event list\n");
		}
		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
		ret = 0;
		goto exit;
@@ -1474,10 +1615,30 @@ int sde_cp_ad_interrupt(struct drm_crtc *crtc_drm, bool en,
		DRM_ERROR("failed to register the callback ret %d\n", ret);
		goto exit;
	}

	if (node) {
		/* device resume or resume from IPC cases */
		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
			ret = sde_core_irq_enable(kms, &irq_idx, 1);
			if (ret) {
				DRM_ERROR("enable irq %d error %d\n",
					irq_idx, ret);
				sde_core_irq_unregister_callback(kms,
					irq_idx, ad_irq);
			} else {
				node->state = IRQ_ENABLED;
			}
		}
	} else {
		/* request from userspace to register the event
		 * in this case, node has not been added into the event list
		 */
		ret = sde_core_irq_enable(kms, &irq_idx, 1);
		if (ret) {
			DRM_ERROR("failed to enable irq ret %d\n", ret);
		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
			sde_core_irq_unregister_callback(kms,
				irq_idx, ad_irq);
		}
	}
exit:
	return ret;
@@ -1540,3 +1701,201 @@ void sde_cp_crtc_post_ipc(struct drm_crtc *drm_crtc)

	sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESUME);
}

static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx)
{
	struct sde_crtc *crtc = arg;
	struct drm_crtc *crtc_drm = &crtc->base;
	struct sde_hw_dspp *hw_dspp;
	struct sde_kms *kms;
	struct sde_crtc_irq_info *node = NULL;
	u32 i;
	int ret = 0;
	unsigned long flags;

	/* disable histogram irq */
	kms = get_kms(crtc_drm);
	spin_lock_irqsave(&crtc->spin_lock, flags);
	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);
	spin_unlock_irqrestore(&crtc->spin_lock, flags);

	if (!node) {
		DRM_ERROR("cannot find histogram event node in crtc\n");
		return;
	}

	if (node->state == IRQ_ENABLED) {
		if (sde_core_irq_disable_nolock(kms, irq_idx)) {
			DRM_ERROR("failed to disable irq %d, ret %d\n",
				irq_idx, ret);
			return;
		}
		node->state = IRQ_DISABLED;
	}

	/* lock histogram buffer */
	for (i = 0; i < crtc->num_mixers; i++) {
		hw_dspp = crtc->mixers[i].hw_dspp;
		if (hw_dspp && hw_dspp->ops.lock_histogram)
			hw_dspp->ops.lock_histogram(hw_dspp, NULL);
	}

	/* notify histogram event */
	sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL);
}

static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg)
{
	struct sde_hw_dspp *hw_dspp = NULL;
	struct sde_crtc *crtc;
	struct drm_event event;
	struct drm_msm_hist *hist_data;
	struct drm_msm_hist tmp_hist_data;
	u32 i, j;

	if (!crtc_drm) {
		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
		return;
	}

	crtc = to_sde_crtc(crtc_drm);
	if (!crtc) {
		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
		return;
	}

	if (!crtc->hist_blob)
		return;

	/* read histogram data into blob */
	hist_data = (struct drm_msm_hist *)crtc->hist_blob->data;
	for (i = 0; i < crtc->num_mixers; i++) {
		hw_dspp = crtc->mixers[i].hw_dspp;
		if (!hw_dspp || !hw_dspp->ops.read_histogram) {
			DRM_ERROR("invalid dspp %pK or read_histogram func\n",
				hw_dspp);
			return;
		}
		if (!i) {
			hw_dspp->ops.read_histogram(hw_dspp, hist_data);
		} else {
			/* Merge hist data for DSPP0 and DSPP1 */
			hw_dspp->ops.read_histogram(hw_dspp, &tmp_hist_data);
			for (j = 0; j < HIST_V_SIZE; j++)
				hist_data->data[j] += tmp_hist_data.data[j];
		}
	}

	/* send histogram event with blob id */
	event.length = sizeof(u32);
	event.type = DRM_EVENT_HISTOGRAM;
	msm_mode_object_event_notify(&crtc_drm->base, crtc_drm->dev,
			&event, (u8 *)(&crtc->hist_blob->base.id));
}

int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
	struct sde_irq_callback *hist_irq)
{
	struct sde_kms *kms = NULL;
	u32 num_mixers;
	struct sde_hw_mixer *hw_lm;
	struct sde_hw_dspp *hw_dspp = NULL;
	struct sde_crtc *crtc;
	struct sde_crtc_irq_info *node = NULL;
	int i, irq_idx, ret = 0;

	if (!crtc_drm || !hist_irq) {
		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq);
		return -EINVAL;
	}

	crtc = to_sde_crtc(crtc_drm);
	if (!crtc) {
		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
		return -EINVAL;
	}

	kms = get_kms(crtc_drm);
	num_mixers = crtc->num_mixers;

	for (i = 0; i < num_mixers; i++) {
		hw_lm = crtc->mixers[i].hw_lm;
		hw_dspp = crtc->mixers[i].hw_dspp;
		if (!hw_lm->cfg.right_mixer)
			break;
	}

	if (!hw_dspp) {
		DRM_ERROR("invalid dspp\n");
		ret = -EINVAL;
		goto exit;
	}

	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
			hw_dspp->idx);
	if (irq_idx < 0) {
		DRM_ERROR("failed to get the irq idx ret %d\n", irq_idx);
		ret = irq_idx;
		goto exit;
	}

	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);

	/* deregister histogram irq */
	if (!en) {
		if (node) {
			/* device suspend case or suspend to IPC cases */
			if (node->state == IRQ_ENABLED) {
				ret = sde_core_irq_disable(kms, &irq_idx, 1);
				if (ret)
					DRM_ERROR("disable irq %d error %d\n",
						irq_idx, ret);
				else
					node->state = IRQ_NOINIT;
			} else {
				node->state = IRQ_NOINIT;
			}
		} else {
			DRM_ERROR("failed to get node from crtc event list\n");
		}

		sde_core_irq_unregister_callback(kms, irq_idx, hist_irq);
		goto exit;
	}

	/* register histogram irq */
	hist_irq->arg = crtc;
	hist_irq->func = sde_cp_hist_interrupt_cb;
	ret = sde_core_irq_register_callback(kms, irq_idx, hist_irq);
	if (ret) {
		DRM_ERROR("failed to register the callback ret %d\n", ret);
		goto exit;
	}

	if (node) {
		/* device resume or resume from IPC cases */
		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
			ret = sde_core_irq_enable(kms, &irq_idx, 1);
			if (ret) {
				DRM_ERROR("enable irq %d error %d\n",
					irq_idx, ret);
				sde_core_irq_unregister_callback(kms,
					irq_idx, hist_irq);
			} else {
				node->state = IRQ_ENABLED;
			}
		}
	} else {
		/* request from userspace to register the event
		 * in this case, node has not been added into the event list
		 */
		ret = sde_core_irq_enable(kms, &irq_idx, 1);
		if (ret) {
			DRM_ERROR("failed to enable irq ret %d\n", ret);
			sde_core_irq_unregister_callback(kms,
				irq_idx, hist_irq);
		}
	}
exit:
	return ret;
}
+28 −0
Original line number Diff line number Diff line
@@ -29,6 +29,25 @@ enum sde_memcolor_type {
	MEMCOLOR_FOLIAGE
};

/*
 * PA HISTOGRAM modes
 * @HIST_DISABLED          Histogram disabled
 * @HIST_ENABLED           Histogram enabled
 */
enum sde_hist_modes {
	HIST_DISABLED,
	HIST_ENABLED
};

/**
 * struct drm_prop_enum_list - drm structure for creating enum property and
 *                             enumerating values
 */
static const struct drm_prop_enum_list sde_hist_modes[] = {
	{HIST_DISABLED, "hist_off"},
	{HIST_ENABLED, "hist_on"},
};

/**
 * sde_cp_crtc_init(): Initialize color processing lists for a crtc.
 *                     Should be called during crtc initialization.
@@ -117,4 +136,13 @@ void sde_cp_crtc_pre_ipc(struct drm_crtc *crtc);
 * @crtc: Pointer to crtc.
 */
void sde_cp_crtc_post_ipc(struct drm_crtc *crtc);

/**
 * sde_cp_hist_interrupt: Api to enable/disable histogram interrupt
 * @crtc: Pointer to crtc.
 * @en: Variable to enable/disable interrupt.
 * @irq: Pointer to irq callback
 */
int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
	struct sde_irq_callback *hist_irq);
#endif /*_SDE_COLOR_PROCESSING_H */
+7 −10
Original line number Diff line number Diff line
@@ -48,14 +48,6 @@
#define MEM_PROTECT_SD_CTRL_SWITCH 0x18
#define MDP_DEVICE_ID            0x1A

struct sde_crtc_irq_info {
	struct sde_irq_callback irq;
	u32 event;
	int (*func)(struct drm_crtc *crtc, bool en,
			struct sde_irq_callback *irq);
	struct list_head list;
};

struct sde_crtc_custom_events {
	u32 event;
	int (*func)(struct drm_crtc *crtc, bool en,
@@ -70,7 +62,8 @@ static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm,
static struct sde_crtc_custom_events custom_events[] = {
	{DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt},
	{DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler},
	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}
	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler},
	{DRM_EVENT_HISTOGRAM, sde_cp_hist_interrupt},
};

/* default input fence timeout, in ms */
@@ -5471,6 +5464,8 @@ static int _sde_crtc_event_enable(struct sde_kms *kms,

	if (!ret) {
		spin_lock_irqsave(&crtc->spin_lock, flags);
		/* irq is regiestered and enabled and set the state */
		node->state = IRQ_ENABLED;
		list_add_tail(&node->list, &crtc->user_event_list);
		spin_unlock_irqrestore(&crtc->spin_lock, flags);
	} else {
@@ -5494,7 +5489,6 @@ static int _sde_crtc_event_disable(struct sde_kms *kms,
	spin_lock_irqsave(&crtc->spin_lock, flags);
	list_for_each_entry(node, &crtc->user_event_list, list) {
		if (node->event == event) {
			list_del(&node->list);
			found = true;
			break;
		}
@@ -5510,12 +5504,15 @@ static int _sde_crtc_event_disable(struct sde_kms *kms,
	 * no need to disable/de-register.
	 */
	if (!crtc_drm->enabled) {
		list_del(&node->list);
		kfree(node);
		return 0;
	}
	priv = kms->dev->dev_private;
	sde_power_resource_enable(&priv->phandle, kms->core_client, true);
	ret = node->func(crtc_drm, false, &node->irq);
	list_del(&node->list);
	kfree(node);
	sde_power_resource_enable(&priv->phandle, kms->core_client, false);
	return ret;
}
+26 −0
Original line number Diff line number Diff line
@@ -285,6 +285,9 @@ struct sde_crtc {
	struct list_head rp_head;

	struct sde_crtc_smmu_state_data smmu_state;

	/* blob for histogram data */
	struct drm_property_blob *hist_blob;
};

#define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
@@ -407,6 +410,29 @@ struct sde_crtc_state {
	struct sde_crtc_respool rp;
};

enum sde_crtc_irq_state {
	IRQ_NOINIT,
	IRQ_ENABLED,
	IRQ_DISABLED,
};

/**
 * sde_crtc_irq_info - crtc interrupt info
 * @irq: interrupt callback
 * @event: event type of the interrupt
 * @func: function pointer to enable/disable the interrupt
 * @list: list of user customized event in crtc
 * @ref_count: reference count for the interrupt
 */
struct sde_crtc_irq_info {
	struct sde_irq_callback irq;
	u32 event;
	int (*func)(struct drm_crtc *crtc, bool en,
			struct sde_irq_callback *irq);
	struct list_head list;
	enum sde_crtc_irq_state state;
};

#define to_sde_crtc_state(x) \
	container_of(x, struct sde_crtc_state, base)

Loading