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

Commit a6d95012 authored by Vinu Deokaran's avatar Vinu Deokaran Committed by Gerrit - the friendly Code Review server
Browse files

msm: mdss: mdp: add input event handler



Attach an input event handler from mdp driver. In static screen cases
with command mode panel, mdp driver will wake up from idle state up on
receiving event from input driver. This will reduce latency during frame
kick off.

Change-Id: I60676915fea6f95a4aad5f49e09123d1a43f70cc
Signed-off-by: default avatarVinu Deokaran <vinud@codeaurora.org>
parent 534e3f6f
Loading
Loading
Loading
Loading
+128 −0
Original line number Diff line number Diff line
@@ -820,6 +820,122 @@ static void mdss_fb_shutdown(struct platform_device *pdev)
	unlock_fb_info(mfd->fbi);
}

static void mdss_fb_input_event_handler(struct input_handle *handle,
				    unsigned int type,
				    unsigned int code,
				    int value)
{
	struct msm_fb_data_type *mfd = handle->handler->private;
	int rc;

	if (type != EV_ABS)
		return;

	if (mfd->mdp.input_event_handler) {
		rc = mfd->mdp.input_event_handler(mfd);
		if (rc)
			pr_err("mdp input event handler failed\n");
	}
}

static int mdss_fb_input_connect(struct input_handler *handler,
			     struct input_dev *dev,
			     const struct input_device_id *id)
{
	int rc;
	struct input_handle *handle;

	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
	if (!handle)
		return -ENOMEM;

	handle->dev = dev;
	handle->handler = handler;
	handle->name = handler->name;

	rc = input_register_handle(handle);
	if (rc) {
		pr_err("failed to register input handle, rc = %d\n", rc);
		goto error;
	}

	rc = input_open_device(handle);
	if (rc) {
		pr_err("failed to open input device, rc = %d\n", rc);
		goto error_unregister;
	}

	return 0;

error_unregister:
	input_unregister_handle(handle);
error:
	kfree(handle);
	return rc;
}

static void mdss_fb_input_disconnect(struct input_handle *handle)
{
	input_close_device(handle);
	input_unregister_handle(handle);
	kfree(handle);
}

/*
 * Structure for specifying event parameters on which to receive callbacks.
 * This structure will trigger a callback in case of a touch event (specified by
 * EV_ABS) where there is a change in X and Y coordinates,
 */
static const struct input_device_id mdss_fb_input_ids[] = {
	{
		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
		.evbit = { BIT_MASK(EV_ABS) },
		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
				BIT_MASK(ABS_MT_POSITION_X) |
				BIT_MASK(ABS_MT_POSITION_Y) },
	},
	{ },
};

static int mdss_fb_register_input_handler(struct msm_fb_data_type *mfd)
{
	int rc;
	struct input_handler *handler;

	if (mfd->input_handler)
		return -EINVAL;

	handler = kzalloc(sizeof(*handler), GFP_KERNEL);
	if (!handler)
		return -ENOMEM;

	handler->event = mdss_fb_input_event_handler;
	handler->connect = mdss_fb_input_connect;
	handler->disconnect = mdss_fb_input_disconnect,
	handler->name = "mdss_fb",
	handler->id_table = mdss_fb_input_ids;
	handler->private = mfd;

	rc = input_register_handler(handler);
	if (rc) {
		pr_err("Unable to register the input handler\n");
		kfree(handler);
	} else {
		mfd->input_handler = handler;
	}

	return rc;
}

static void mdss_fb_unregister_input_handler(struct msm_fb_data_type *mfd)
{
	if (!mfd->input_handler)
		return;

	input_unregister_handler(mfd->input_handler);
	kfree(mfd->input_handler);
}

static int mdss_fb_probe(struct platform_device *pdev)
{
	struct msm_fb_data_type *mfd = NULL;
@@ -938,6 +1054,16 @@ static int mdss_fb_probe(struct platform_device *pdev)
	if (mfd->mdp.splash_init_fnc)
		mfd->mdp.splash_init_fnc(mfd);

	/*
	 * Register with input driver for a callback for command mode panels.
	 * When there is an input event, mdp clocks will be turned on to reduce
	 * latency when a frame update happens.
	 * Video mode panels are not handled today because clocks are always on.
	 */
	if (mfd->panel_info->type == MIPI_CMD_PANEL)
		if (mdss_fb_register_input_handler(mfd))
			pr_err("failed to register input handler\n");

	INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work);

	return rc;
@@ -981,6 +1107,8 @@ static int mdss_fb_remove(struct platform_device *pdev)
	if (mfd->key != MFD_KEY)
		return -EINVAL;

	mdss_fb_unregister_input_handler(mfd);

	if (mdss_fb_suspend_sub(mfd))
		pr_err("msm_fb_remove: can't stop the device %d\n",
			    mfd->index);
+2 −0
Original line number Diff line number Diff line
@@ -219,6 +219,7 @@ struct msm_mdp_interface {
	void (*check_dsi_status)(struct work_struct *work, uint32_t interval);
	int (*configure_panel)(struct msm_fb_data_type *mfd, int mode,
				int dest_ctrl);
	int (*input_event_handler)(struct msm_fb_data_type *mfd);
	void *private1;
};

@@ -341,6 +342,7 @@ struct msm_fb_data_type {
	u32 switch_new_mode;
	bool pending_switch;
	struct mutex switch_lock;
	struct input_handler *input_handler;
};

static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd)
+3 −0
Original line number Diff line number Diff line
@@ -232,6 +232,7 @@ struct mdss_mdp_ctl_intfs_ops {
	int (*config_fps_fnc)(struct mdss_mdp_ctl *ctl,
				struct mdss_mdp_ctl *sctl, int new_fps);
	int (*restore_fnc)(struct mdss_mdp_ctl *ctl);
	int (*early_wake_up_fnc)(struct mdss_mdp_ctl *ctl);
};

struct mdss_mdp_ctl {
@@ -312,6 +313,8 @@ struct mdss_mdp_ctl {

	struct mdss_mdp_ctl_intfs_ops ops;
	bool force_ctl_start;

	u64 last_input_time;
};

struct mdss_mdp_mixer {
+109 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#define STOP_TIMEOUT(hz) msecs_to_jiffies((1000 / hz) * (6 + 2))
#define POWER_COLLAPSE_TIME msecs_to_jiffies(100)
#define CMD_MODE_IDLE_TIMEOUT msecs_to_jiffies(16 * 4)
#define INPUT_EVENT_HANDLER_DELAY_USECS (16000 * 4)

static DEFINE_MUTEX(cmd_clk_mtx);

@@ -52,6 +53,7 @@ struct mdss_mdp_cmd_ctx {
	struct work_struct gate_clk_work;
	struct delayed_work delayed_off_clk_work;
	struct work_struct pp_done_work;
	struct work_struct early_wakeup_clk_work;
	struct mutex autorefresh_mtx;
	atomic_t pp_done_cnt;

@@ -312,11 +314,17 @@ err:
 *	pending data transfer and turn off all the clocks/resources,
 *	so after return from this event we must be in off
 *	state.
 *
 * @MDP_RSRC_CTL_EVENT_EARLY_WAKE_UP:
 *	This event happens at NORMAL priority from a work item.
 *	Event signals that there will be a frame update soon and mdp should wake
 *	up early to update the frame with little latency.
 */
enum mdp_rsrc_ctl_events {
	MDP_RSRC_CTL_EVENT_KICKOFF = 1,
	MDP_RSRC_CTL_EVENT_PP_DONE,
	MDP_RSRC_CTL_EVENT_STOP
	MDP_RSRC_CTL_EVENT_STOP,
	MDP_RSRC_CTL_EVENT_EARLY_WAKE_UP
};

enum {
@@ -335,6 +343,8 @@ static char *get_sw_event_name(u32 sw_event)
		return "PP_DONE";
	case MDP_RSRC_CTL_EVENT_STOP:
		return "STOP";
	case MDP_RSRC_CTL_EVENT_EARLY_WAKE_UP:
		return "EARLY_WAKE_UP";
	default:
		return "UNKNOWN";
	}
@@ -655,6 +665,51 @@ int mdss_mdp_resource_control(struct mdss_mdp_ctl *ctl, u32 sw_event)
			mdp5_data->resources_state = MDP_RSRC_CTL_STATE_OFF;
		}
		break;
	case MDP_RSRC_CTL_EVENT_EARLY_WAKE_UP:
		/*
		 * 1. If the current state is ON, stay in ON and cancel any
		 *    pending GATE work item.
		 * 2. If the current state is GATED, stay at GATED and cancel
		 *    any pending POWER-OFF work item.
		 * 3. If the current state is POWER-OFF, Schedule a work item to
		 *    POWER-ON.
		 */
		if (mdp5_data->resources_state != MDP_RSRC_CTL_STATE_OFF) {
			if (cancel_work_sync(&ctx->gate_clk_work))
				pr_debug("%s: %s - gate_work cancelled\n",
					 __func__, get_sw_event_name(sw_event));

			if (cancel_delayed_work_sync(
					&ctx->delayed_off_clk_work))
				pr_debug("%s: %s - off work cancelled\n",
					 __func__, get_sw_event_name(sw_event));
		} else {
			mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
			mdss_mdp_ctl_intf_event(ctx->ctl,
						MDSS_EVENT_PANEL_CLK_CTRL,
						(void *)MDSS_DSI_CLK_ON,
						true);
			if (sctx)
				mdss_mdp_ctl_intf_event(sctx->ctl,
						      MDSS_EVENT_PANEL_CLK_CTRL,
						      (void *)MDSS_DSI_CLK_ON,
						      true);

			mdss_mdp_cmd_clk_on(ctx);
			if (sctx)
				mdss_mdp_cmd_clk_on(sctx);

			mdp5_data->resources_state = MDP_RSRC_CTL_STATE_ON;

			/*
			 * Schedule off work after cmd mode idle timeout is
			 * reached. This is to prevent the case where early wake
			 * up is called but no frame update is sent.
			 */
			schedule_delayed_work(&ctx->delayed_off_clk_work,
				      CMD_MODE_IDLE_TIMEOUT);
		}
		break;
	default:
		pr_warn("%s unexpected event (%d)\n", __func__, sw_event);
		break;
@@ -913,7 +968,7 @@ static void clk_ctrl_delayed_off_work(struct work_struct *work)

		if (mdp5_data->resources_state
			!= MDP_RSRC_CTL_STATE_GATE)
			pr_warn("enter off from unexpected state\n");
			pr_debug("%s: enter off from ON state\n", __func__);
	}

	/* first power off the slave DSI  (if present) */
@@ -1834,6 +1889,56 @@ end:
	return ret;
}

static void early_wakeup_work(struct work_struct *work)
{
	int rc = 0;
	struct mdss_mdp_cmd_ctx *ctx =
		container_of(work, typeof(*ctx), early_wakeup_clk_work);
	struct mdss_mdp_ctl *ctl;

	if (!ctx) {
		pr_err("%s: invalid ctx\n", __func__);
		return;
	}

	ATRACE_BEGIN(__func__);
	ctl = ctx->ctl;

	if (!ctl) {
		pr_err("%s: invalid ctl\n", __func__);
		goto fail;
	}

	rc = mdss_mdp_resource_control(ctl, MDP_RSRC_CTL_EVENT_EARLY_WAKE_UP);
	if (rc)
		pr_err("%s: failed to control resources\n", __func__);

fail:
	ATRACE_END(__func__);
}

static int mdss_mdp_cmd_early_wake_up(struct mdss_mdp_ctl *ctl)
{
	u64 curr_time;
	struct mdss_mdp_cmd_ctx *ctx;

	curr_time = ktime_to_us(ktime_get());

	if ((curr_time - ctl->last_input_time) <
			INPUT_EVENT_HANDLER_DELAY_USECS)
		return 0;
	ctl->last_input_time = curr_time;

	ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
	/*
	 * Early wake up event is called from an interrupt context and
	 * involves cancelling queued work items. So this will be
	 * scheduled in a work item.
	 */
	schedule_work(&ctx->early_wakeup_clk_work);
	return 0;
}

static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
	struct mdss_mdp_cmd_ctx *ctx, int pp_num,
	int pingpong_split_slave)
@@ -1857,6 +1962,7 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
	INIT_DELAYED_WORK(&ctx->delayed_off_clk_work,
		clk_ctrl_delayed_off_work);
	INIT_WORK(&ctx->pp_done_work, pingpong_done_work);
	INIT_WORK(&ctx->early_wakeup_clk_work, early_wakeup_work);
	atomic_set(&ctx->pp_done_cnt, 0);
	ctx->autorefresh_off_pending = false;
	ctx->autorefresh_init = false;
@@ -2030,6 +2136,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
	ctl->ops.remove_vsync_handler = mdss_mdp_cmd_remove_vsync_handler;
	ctl->ops.read_line_cnt_fnc = mdss_mdp_cmd_line_count;
	ctl->ops.restore_fnc = mdss_mdp_cmd_restore;
	ctl->ops.early_wake_up_fnc = mdss_mdp_cmd_early_wake_up;
	pr_debug("%s:-\n", __func__);

	return 0;
+12 −0
Original line number Diff line number Diff line
@@ -4792,6 +4792,17 @@ static int mdss_mdp_update_panel_info(struct msm_fb_data_type *mfd,
	return 0;
}

int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd)
{
	int rc = 0;
	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);

	if (ctl->ops.early_wake_up_fnc)
		rc = ctl->ops.early_wake_up_fnc(ctl);

	return rc;
}

int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
	struct device *dev = mfd->fbi->dev;
@@ -4831,6 +4842,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
	mdp5_interface->get_sync_fnc = mdss_mdp_rotator_sync_pt_get;
	mdp5_interface->splash_init_fnc = mdss_mdp_splash_init;
	mdp5_interface->configure_panel = mdss_mdp_update_panel_info;
	mdp5_interface->input_event_handler = mdss_mdp_input_event_handler;

	if (mfd->panel_info->type == WRITEBACK_PANEL) {
		mdp5_interface->atomic_validate =