|
@@ -28,6 +28,8 @@
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/slab.h>
|
|
|
|
|
|
+#include <media/rc-core.h>
|
|
|
+
|
|
|
#include "sil-sii8620.h"
|
|
|
|
|
|
#define SII8620_BURST_BUF_LEN 288
|
|
@@ -58,6 +60,7 @@ enum sii8620_mt_state {
|
|
|
struct sii8620 {
|
|
|
struct drm_bridge bridge;
|
|
|
struct device *dev;
|
|
|
+ struct rc_dev *rc_dev;
|
|
|
struct clk *clk_xtal;
|
|
|
struct gpio_desc *gpio_reset;
|
|
|
struct gpio_desc *gpio_int;
|
|
@@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
|
|
|
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
|
|
|
}
|
|
|
|
|
|
+static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code)
|
|
|
+{
|
|
|
+ sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code);
|
|
|
+}
|
|
|
+
|
|
|
+static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code)
|
|
|
+{
|
|
|
+ sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code);
|
|
|
+}
|
|
|
+
|
|
|
static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
|
|
|
struct sii8620_mt_msg *msg)
|
|
|
{
|
|
@@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx)
|
|
|
sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf));
|
|
|
}
|
|
|
|
|
|
+static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
|
|
|
+{
|
|
|
+ bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK);
|
|
|
+
|
|
|
+ scancode &= MHL_RCP_KEY_ID_MASK;
|
|
|
+
|
|
|
+ if (!ctx->rc_dev) {
|
|
|
+ dev_dbg(ctx->dev, "RCP input device not initialized\n");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pressed)
|
|
|
+ rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
|
|
|
+ else
|
|
|
+ rc_keyup(ctx->rc_dev);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
|
|
|
{
|
|
|
u8 ints[MHL_INT_SIZE];
|
|
@@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx)
|
|
|
|
|
|
static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
|
|
|
{
|
|
|
- struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
|
|
|
+ struct sii8620_mt_msg *msg;
|
|
|
u8 buf[2];
|
|
|
|
|
|
- if (!msg)
|
|
|
- return;
|
|
|
-
|
|
|
sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
|
|
|
|
|
|
switch (buf[0]) {
|
|
|
case MHL_MSC_MSG_RAPK:
|
|
|
+ msg = sii8620_msc_msg_first(ctx);
|
|
|
+ if (!msg)
|
|
|
+ return;
|
|
|
msg->ret = buf[1];
|
|
|
ctx->mt_state = MT_STATE_DONE;
|
|
|
break;
|
|
|
+ case MHL_MSC_MSG_RCP:
|
|
|
+ if (!sii8620_rcp_consume(ctx, buf[1]))
|
|
|
+ sii8620_mt_rcpe(ctx,
|
|
|
+ MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
|
|
|
+ sii8620_mt_rcpk(ctx, buf[1]);
|
|
|
+ break;
|
|
|
default:
|
|
|
dev_err(ctx->dev, "%s message type %d,%d not supported",
|
|
|
__func__, buf[0], buf[1]);
|
|
@@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx)
|
|
|
enable_irq(to_i2c_client(ctx->dev)->irq);
|
|
|
}
|
|
|
|
|
|
+static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
|
|
|
+{
|
|
|
+ struct rc_dev *rc_dev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
|
|
+ if (!rc_dev) {
|
|
|
+ dev_err(ctx->dev, "Failed to allocate RC device\n");
|
|
|
+ ctx->error = -ENOMEM;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc_dev->input_phys = "sii8620/input0";
|
|
|
+ rc_dev->input_id.bustype = BUS_VIRTUAL;
|
|
|
+ rc_dev->map_name = RC_MAP_CEC;
|
|
|
+ rc_dev->allowed_protocols = RC_PROTO_BIT_CEC;
|
|
|
+ rc_dev->driver_name = "sii8620";
|
|
|
+ rc_dev->device_name = "sii8620";
|
|
|
+
|
|
|
+ ret = rc_register_device(rc_dev);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ dev_err(ctx->dev, "Failed to register RC device\n");
|
|
|
+ ctx->error = ret;
|
|
|
+ rc_free_device(ctx->rc_dev);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ctx->rc_dev = rc_dev;
|
|
|
+}
|
|
|
+
|
|
|
static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
|
|
|
{
|
|
|
return container_of(bridge, struct sii8620, bridge);
|
|
|
}
|
|
|
|
|
|
+static int sii8620_attach(struct drm_bridge *bridge)
|
|
|
+{
|
|
|
+ struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
|
|
+
|
|
|
+ sii8620_init_rcp_input_dev(ctx);
|
|
|
+
|
|
|
+ return sii8620_clear_error(ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static void sii8620_detach(struct drm_bridge *bridge)
|
|
|
+{
|
|
|
+ struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
|
|
+
|
|
|
+ rc_unregister_device(ctx->rc_dev);
|
|
|
+}
|
|
|
+
|
|
|
static bool sii8620_mode_fixup(struct drm_bridge *bridge,
|
|
|
const struct drm_display_mode *mode,
|
|
|
struct drm_display_mode *adjusted_mode)
|
|
@@ -2151,6 +2235,8 @@ end:
|
|
|
}
|
|
|
|
|
|
static const struct drm_bridge_funcs sii8620_bridge_funcs = {
|
|
|
+ .attach = sii8620_attach,
|
|
|
+ .detach = sii8620_detach,
|
|
|
.mode_fixup = sii8620_mode_fixup,
|
|
|
};
|
|
|
|
|
@@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client)
|
|
|
struct sii8620 *ctx = i2c_get_clientdata(client);
|
|
|
|
|
|
disable_irq(to_i2c_client(ctx->dev)->irq);
|
|
|
- drm_bridge_remove(&ctx->bridge);
|
|
|
sii8620_hw_off(ctx);
|
|
|
+ drm_bridge_remove(&ctx->bridge);
|
|
|
|
|
|
return 0;
|
|
|
}
|