video: sii9234: dynamically register and unregister input device

The RCP input device should only be registered as long as MHL is
connected

Change-Id: I07cc3cd1c2439a6ed5685227d26a6b02ab022bea
Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/drivers/video/sii9234.c b/drivers/video/sii9234.c
index 2a07e11..74f8751 100644
--- a/drivers/video/sii9234.c
+++ b/drivers/video/sii9234.c
@@ -46,6 +46,9 @@
 #define T_WAIT_TIMEOUT_RSEN_INT		200
 #define T_SRC_RXSENSE_DEGLITCH		110
 
+/* MHL feature flags */
+#define MHL_FEATURE_FLAG_RCP_SUPPORT	(1 << 0)
+
 /* MHL TX Addr 0x72 Registers */
 #define MHL_TX_IDL_REG			0x02
 #define MHL_TX_IDH_REG			0x03
@@ -538,6 +541,7 @@
 	struct list_head		msc_data_list;
 
 	struct input_dev		*input_dev;
+	struct mutex			input_lock;
 	u16				keycode[SII9234_RCP_NUM_KEYS];
 
 	struct work_struct		redetect_work;
@@ -884,6 +888,62 @@
 	hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
 }
 
+static int sii9234_register_input_device(struct sii9234_data *sii9234)
+{
+	struct input_dev *input;
+	int ret;
+	u8 i;
+
+	input = input_allocate_device();
+	if (!input) {
+		pr_err("sii9234: failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	/* indicate that we generate key events */
+	set_bit(EV_KEY, input->evbit);
+	memcpy(sii9234->keycode, sii9234_rcp_def_keymap,
+			SII9234_RCP_NUM_KEYS *
+				sizeof(sii9234_rcp_def_keymap[0]));
+	input->keycode = sii9234->keycode;
+	input->keycodemax = SII9234_RCP_NUM_KEYS;
+	input->keycodesize = sizeof(sii9234->keycode[0]);
+	for (i = 0; i < SII9234_RCP_NUM_KEYS; i++) {
+		u16 keycode = sii9234->keycode[i];
+		if (keycode != KEY_UNKNOWN && keycode != KEY_RESERVED)
+			set_bit(keycode, input->keybit);
+	}
+
+	input_set_drvdata(input, sii9234);
+	input->name = "sii9234_rcp";
+	input->id.bustype = BUS_I2C;
+
+	pr_debug("sii9234: registering input device\n");
+	ret = input_register_device(input);
+	if (ret < 0) {
+		pr_err("sii9234: failed to register input device\n");
+		input_free_device(input);
+		return ret;
+	}
+
+	mutex_lock(&sii9234->input_lock);
+	sii9234->input_dev = input;
+	mutex_unlock(&sii9234->input_lock);
+
+	return 0;
+}
+
+static void sii9234_unregister_input_device(struct sii9234_data *sii9234)
+{
+	mutex_lock(&sii9234->input_lock);
+	if (sii9234->input_dev) {
+		pr_debug("sii9234: unregistering input device\n");
+		input_unregister_device(sii9234->input_dev);
+		sii9234->input_dev = NULL;
+	}
+	mutex_unlock(&sii9234->input_lock);
+}
+
 static void sii9234_mhl_tx_ctl_int(struct sii9234_data *sii9234)
 {
 	mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL1_REG, 0xD0);
@@ -897,6 +957,7 @@
 	if (sii9234->claimed)
 		sii9234->pdata->connect(false, NULL);
 
+	sii9234_unregister_input_device(sii9234);
 	sii9234->state = STATE_DISCONNECTED;
 	sii9234->claimed = false;
 
@@ -1248,6 +1309,9 @@
 
 	sii9234->claimed = true;
 	sii9234->pdata->connect(true, ret >= 0 ? sii9234->devcap : NULL);
+	if (sii9234->devcap[MHL_DEVCAP_FEATURE_FLAG] &
+			MHL_FEATURE_FLAG_RCP_SUPPORT)
+		sii9234_register_input_device(sii9234);
 	mutex_unlock(&sii9234->lock);
 
 	return OTG_ID_HANDLED;
@@ -1303,9 +1367,13 @@
 static void rcp_key_report(struct sii9234_data *sii9234, u16 key)
 {
 	pr_debug("sii9234: report rcp key: %d\n", key);
-	input_report_key(sii9234->input_dev, key, 1);
-	input_report_key(sii9234->input_dev, key, 0);
-	input_sync(sii9234->input_dev);
+	mutex_lock(&sii9234->input_lock);
+	if (sii9234->input_dev) {
+		input_report_key(sii9234->input_dev, key, 1);
+		input_report_key(sii9234->input_dev, key, 0);
+		input_sync(sii9234->input_dev);
+	}
+	mutex_unlock(&sii9234->input_lock);
 }
 
 static void cbus_process_rcp_key(struct sii9234_data *sii9234, u8 key)
@@ -1933,9 +2001,7 @@
 {
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 	struct sii9234_data *sii9234;
-	struct input_dev *input;
 	int ret;
-	u8 i;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		return -EIO;
@@ -1946,18 +2012,11 @@
 		return -ENOMEM;
 	}
 
-	input = input_allocate_device();
-	if (!input) {
-		dev_err(&client->dev, "failed to allocate input device.\n");
-		ret = -ENOMEM;
-		goto err_exit0;
-	}
-
 	sii9234->pdata = client->dev.platform_data;
 	sii9234->pdata->mhl_tx_client = client;
 	if (!sii9234->pdata) {
 		ret = -EINVAL;
-		goto err_exit1;
+		goto err_exit;
 	}
 
 	i2c_set_clientdata(client, sii9234);
@@ -1967,6 +2026,7 @@
 	init_waitqueue_head(&sii9234->wq);
 	mutex_init(&sii9234->lock);
 	mutex_init(&sii9234->msc_lock);
+	mutex_init(&sii9234->input_lock);
 
 	INIT_WORK(&sii9234->msc_work, sii9234_msc_event);
 	INIT_LIST_HEAD(&sii9234->msc_data_list);
@@ -1975,7 +2035,7 @@
 				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
 				   "sii9234", sii9234);
 	if (ret < 0)
-		goto err_exit1;
+		goto err_exit;
 
 	disable_irq(client->irq);
 
@@ -1983,53 +2043,23 @@
 	sii9234->otg_id_nb.cancel = sii9234_cancel_callback;
 	sii9234->otg_id_nb.priority = sii9234->pdata->prio;
 
-	/* indicate that we generate key events */
-	set_bit(EV_KEY, input->evbit);
-	memcpy(sii9234->keycode, sii9234_rcp_def_keymap,
-			SII9234_RCP_NUM_KEYS *
-				sizeof(sii9234_rcp_def_keymap[0]));
-	input->keycode = sii9234->keycode;
-	input->keycodemax = SII9234_RCP_NUM_KEYS;
-	input->keycodesize = sizeof(sii9234->keycode[0]);
-	for (i = 0; i < SII9234_RCP_NUM_KEYS; i++) {
-		u16 keycode = sii9234->keycode[i];
-		if (keycode != KEY_UNKNOWN && keycode != KEY_RESERVED)
-			set_bit(keycode, input->keybit);
-	}
-
-	sii9234->input_dev = input;
-	input_set_drvdata(input, sii9234);
-	input->name = "sii9234_rcp";
-	input->id.bustype = BUS_I2C;
-
-	ret = input_register_device(input);
-	if (ret < 0) {
-		dev_err(&client->dev, "fail to register input device\n");
-		goto err_exit1;
-	}
-
 	plist_node_init(&sii9234->otg_id_nb.p, sii9234->pdata->prio);
 
 	ret = otg_id_register_notifier(&sii9234->otg_id_nb);
 	if (ret < 0) {
 		dev_err(&client->dev, "Unable to register notifier\n");
-		goto err_exit2;
+		goto err_exit;
 	}
 
 	sii9234->redetect_wq = create_singlethread_workqueue("sii9234");
 	if (!sii9234->redetect_wq) {
 		dev_err(&client->dev, "unable to create workqueue\n");
-		goto err_exit2;
+		goto err_exit;
 	}
 	INIT_WORK(&sii9234->redetect_work, sii9234_retry_detection);
 	return 0;
 
-err_exit2:
-	input_unregister_device(input);
-	goto err_exit0;
-err_exit1:
-	input_free_device(input);
-err_exit0:
+err_exit:
 	kfree(sii9234);
 	return ret;
 }