Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/bridge/sii8620: add remote control support

MHL specification defines Remote Control Protocol(RCP) to
send input events between MHL devices.
The driver now recognizes RCP messages and reacts to them
by reporting key events to input subsystem, allowing
a user to control a device using TV remote control.

Signed-off-by: Maciej Purski <m.purski@samsung.com>
Acked-by: Sean Young <sean@mess.org>
Acked-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1503565087-19730-1-git-send-email-m.purski@samsung.com

authored by

Maciej Purski and committed by
Andrzej Hajda
e25f1f7c da184dee

+96 -6
+1 -1
drivers/gpu/drm/bridge/Kconfig
··· 71 71 72 72 config DRM_SIL_SII8620 73 73 tristate "Silicon Image SII8620 HDMI/MHL bridge" 74 - depends on OF 74 + depends on OF && RC_CORE 75 75 select DRM_KMS_HELPER 76 76 help 77 77 Silicon Image SII8620 HDMI/MHL bridge chip driver.
+91 -5
drivers/gpu/drm/bridge/sil-sii8620.c
··· 28 28 #include <linux/regulator/consumer.h> 29 29 #include <linux/slab.h> 30 30 31 + #include <media/rc-core.h> 32 + 31 33 #include "sil-sii8620.h" 32 34 33 35 #define SII8620_BURST_BUF_LEN 288 ··· 60 58 struct sii8620 { 61 59 struct drm_bridge bridge; 62 60 struct device *dev; 61 + struct rc_dev *rc_dev; 63 62 struct clk *clk_xtal; 64 63 struct gpio_desc *gpio_reset; 65 64 struct gpio_desc *gpio_int; ··· 432 429 static void sii8620_mt_rap(struct sii8620 *ctx, u8 code) 433 430 { 434 431 sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code); 432 + } 433 + 434 + static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code) 435 + { 436 + sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code); 437 + } 438 + 439 + static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code) 440 + { 441 + sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code); 435 442 } 436 443 437 444 static void sii8620_mt_read_devcap_send(struct sii8620 *ctx, ··· 1766 1753 sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); 1767 1754 } 1768 1755 1756 + static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode) 1757 + { 1758 + bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK); 1759 + 1760 + scancode &= MHL_RCP_KEY_ID_MASK; 1761 + 1762 + if (!ctx->rc_dev) { 1763 + dev_dbg(ctx->dev, "RCP input device not initialized\n"); 1764 + return false; 1765 + } 1766 + 1767 + if (pressed) 1768 + rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0); 1769 + else 1770 + rc_keyup(ctx->rc_dev); 1771 + 1772 + return true; 1773 + } 1774 + 1769 1775 static void sii8620_msc_mr_set_int(struct sii8620 *ctx) 1770 1776 { 1771 1777 u8 ints[MHL_INT_SIZE]; ··· 1836 1804 1837 1805 static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx) 1838 1806 { 1839 - struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx); 1807 + struct sii8620_mt_msg *msg; 1840 1808 u8 buf[2]; 1841 - 1842 - if (!msg) 1843 - return; 1844 1809 1845 1810 sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2); 1846 1811 1847 1812 switch (buf[0]) { 1848 1813 case MHL_MSC_MSG_RAPK: 1814 + msg = sii8620_msc_msg_first(ctx); 1815 + if (!msg) 1816 + return; 1849 1817 msg->ret = buf[1]; 1850 1818 ctx->mt_state = MT_STATE_DONE; 1819 + break; 1820 + case MHL_MSC_MSG_RCP: 1821 + if (!sii8620_rcp_consume(ctx, buf[1])) 1822 + sii8620_mt_rcpe(ctx, 1823 + MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE); 1824 + sii8620_mt_rcpk(ctx, buf[1]); 1851 1825 break; 1852 1826 default: 1853 1827 dev_err(ctx->dev, "%s message type %d,%d not supported", ··· 2140 2102 enable_irq(to_i2c_client(ctx->dev)->irq); 2141 2103 } 2142 2104 2105 + static void sii8620_init_rcp_input_dev(struct sii8620 *ctx) 2106 + { 2107 + struct rc_dev *rc_dev; 2108 + int ret; 2109 + 2110 + rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); 2111 + if (!rc_dev) { 2112 + dev_err(ctx->dev, "Failed to allocate RC device\n"); 2113 + ctx->error = -ENOMEM; 2114 + return; 2115 + } 2116 + 2117 + rc_dev->input_phys = "sii8620/input0"; 2118 + rc_dev->input_id.bustype = BUS_VIRTUAL; 2119 + rc_dev->map_name = RC_MAP_CEC; 2120 + rc_dev->allowed_protocols = RC_PROTO_BIT_CEC; 2121 + rc_dev->driver_name = "sii8620"; 2122 + rc_dev->device_name = "sii8620"; 2123 + 2124 + ret = rc_register_device(rc_dev); 2125 + 2126 + if (ret) { 2127 + dev_err(ctx->dev, "Failed to register RC device\n"); 2128 + ctx->error = ret; 2129 + rc_free_device(ctx->rc_dev); 2130 + return; 2131 + } 2132 + ctx->rc_dev = rc_dev; 2133 + } 2134 + 2143 2135 static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) 2144 2136 { 2145 2137 return container_of(bridge, struct sii8620, bridge); 2138 + } 2139 + 2140 + static int sii8620_attach(struct drm_bridge *bridge) 2141 + { 2142 + struct sii8620 *ctx = bridge_to_sii8620(bridge); 2143 + 2144 + sii8620_init_rcp_input_dev(ctx); 2145 + 2146 + return sii8620_clear_error(ctx); 2147 + } 2148 + 2149 + static void sii8620_detach(struct drm_bridge *bridge) 2150 + { 2151 + struct sii8620 *ctx = bridge_to_sii8620(bridge); 2152 + 2153 + rc_unregister_device(ctx->rc_dev); 2146 2154 } 2147 2155 2148 2156 static bool sii8620_mode_fixup(struct drm_bridge *bridge, ··· 2235 2151 } 2236 2152 2237 2153 static const struct drm_bridge_funcs sii8620_bridge_funcs = { 2154 + .attach = sii8620_attach, 2155 + .detach = sii8620_detach, 2238 2156 .mode_fixup = sii8620_mode_fixup, 2239 2157 }; 2240 2158 ··· 2303 2217 struct sii8620 *ctx = i2c_get_clientdata(client); 2304 2218 2305 2219 disable_irq(to_i2c_client(ctx->dev)->irq); 2306 - drm_bridge_remove(&ctx->bridge); 2307 2220 sii8620_hw_off(ctx); 2221 + drm_bridge_remove(&ctx->bridge); 2308 2222 2309 2223 return 0; 2310 2224 }
+4
include/drm/bridge/mhl.h
··· 262 262 #define MHL_RAPK_UNSUPPORTED 0x02 /* Rcvd RAP action code not supported */ 263 263 #define MHL_RAPK_BUSY 0x03 /* Responder too busy to respond */ 264 264 265 + /* Bit masks for RCP messages */ 266 + #define MHL_RCP_KEY_RELEASED_MASK 0x80 267 + #define MHL_RCP_KEY_ID_MASK 0x7F 268 + 265 269 /* 266 270 * Error status codes for RCPE messages 267 271 */