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

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

Merge "msm: mdss: mdp: add input event handler"

parents d1dd7754 a6d95012
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";
	}
@@ -668,6 +678,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;
@@ -920,7 +975,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) */
@@ -1845,6 +1900,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)
@@ -1868,6 +1973,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;
@@ -2041,6 +2147,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
@@ -4802,6 +4802,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;
@@ -4841,6 +4852,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 =