summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorVenkata Prahlad Valluru <vvalluru@codeaurora.org>2020-03-06 17:00:41 +0530
committerVenkata Prahlad Valluru <vvalluru@codeaurora.org>2020-03-06 17:00:41 +0530
commitffd87dde76f6c9b6a134c981c877c2e25c350ea2 (patch)
treeb57664c2da6b5eb88d84c916373c60acfaac46a6 /drivers/video/fbdev
parent80774a56dbc0e7f3aba6d9aa1f3742a42869bc4c (diff)
mdss: msm: hdmi: use circular buffer for CEC rx
There's only one CEC message buffer which could be overwritten if the next interrupt happens before the workthread reads the buffer. To resolve this, defined 4 circular buffers (practially 3 due to linux circular buffer limitation) to rotate the buffer. Since CEC is slow bus, 3 buffers should be enough to avoid potential message overwriting. Change-Id: I538c5902e95edb2231077c1c03a73f690ce68f1a Signed-off-by: Venkata Prahlad Valluru <vvalluru@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_cec.c81
1 files changed, 56 insertions, 25 deletions
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
index cafa4e207332..872e3594715f 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/input.h>
+#include <linux/circ_buf.h>
#include "mdss_hdmi_cec.h"
#include "mdss_panel.h"
@@ -33,6 +34,9 @@
#define CEC_OP_KEY_PRESS 0x44
#define CEC_OP_STANDBY 0x36
+#define CEC_RECV_Q_SIZE 4
+#define CEC_RECV_Q_MASK 3
+
struct hdmi_cec_ctrl {
bool cec_enabled;
bool cec_wakeup_en;
@@ -40,7 +44,9 @@ struct hdmi_cec_ctrl {
bool cec_clear_logical_addr;
u32 cec_msg_wr_status;
- struct cec_msg recv_msg;
+ struct cec_msg recv_msg[CEC_RECV_Q_SIZE];
+ u32 head;
+ u32 tail;
spinlock_t lock;
struct work_struct cec_read_work;
struct completion cec_msg_wr_done;
@@ -172,6 +178,8 @@ static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl)
struct cec_msg *msg;
u32 data;
int i;
+ u32 head;
+ u32 tail;
if (!cec_ctrl || !cec_ctrl->init_data.io) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -183,7 +191,15 @@ static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl)
return -ENODEV;
}
- msg = &cec_ctrl->recv_msg;
+ head = cec_ctrl->head;
+ tail = READ_ONCE(cec_ctrl->tail);
+ if (CIRC_SPACE(head, tail, CEC_RECV_Q_SIZE) < 1) {
+ DEV_ERR("%s: no more space to hold the buffer\n", __func__);
+ return 0; /* Let's just kick the thread */
+ }
+
+ msg = &cec_ctrl->recv_msg[head];
+
io = cec_ctrl->init_data.io;
data = DSS_REG_R(io, HDMI_CEC_RD_DATA);
@@ -220,6 +236,9 @@ static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl)
if (cec_ctrl->cec_clear_logical_addr)
return -EINVAL;
+ /* Update head */
+ smp_store_release(&cec_ctrl->head, (head + 1) & CEC_RECV_Q_MASK);
+
DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__,
msg->opcode, cec_ctrl->cec_wakeup_en,
cec_ctrl->cec_device_suspend);
@@ -232,6 +251,8 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
struct hdmi_cec_ctrl *cec_ctrl = NULL;
struct cec_msg msg;
struct cec_cbs *cbs;
+ u32 head;
+ u32 tail;
cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work);
if (!cec_ctrl || !cec_ctrl->init_data.io) {
@@ -240,31 +261,41 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
}
cbs = cec_ctrl->init_data.cbs;
- memcpy(&msg, &cec_ctrl->recv_msg, sizeof(msg));
-
- if ((msg.opcode == CEC_OP_SET_STREAM_PATH ||
- msg.opcode == CEC_OP_KEY_PRESS) &&
- cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
- cec_ctrl->cec_device_suspend) {
- DEV_DBG("%s: Sending power on at wakeup\n", __func__);
- input_report_key(cec_ctrl->input, KEY_POWER, 1);
- input_sync(cec_ctrl->input);
- input_report_key(cec_ctrl->input, KEY_POWER, 0);
- input_sync(cec_ctrl->input);
- }
- if ((msg.opcode == CEC_OP_STANDBY) &&
- cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
- !cec_ctrl->cec_device_suspend) {
- DEV_DBG("%s: Sending power off on standby\n", __func__);
- input_report_key(cec_ctrl->input, KEY_POWER, 1);
- input_sync(cec_ctrl->input);
- input_report_key(cec_ctrl->input, KEY_POWER, 0);
- input_sync(cec_ctrl->input);
- }
+ /* Read head before reading contents */
+ head = smp_load_acquire(&cec_ctrl->head);
+ tail = cec_ctrl->tail;
+ while (CIRC_CNT(head, tail, CEC_RECV_Q_SIZE) >= 1) {
+ memcpy(&msg, &cec_ctrl->recv_msg[tail], sizeof(msg));
+ tail = (tail + 1) & CEC_RECV_Q_MASK;
+
+ /* Finishing reading before incrementing tail */
+ smp_store_release(&cec_ctrl->tail, tail);
+
+ if ((msg.opcode == CEC_OP_SET_STREAM_PATH ||
+ msg.opcode == CEC_OP_KEY_PRESS) &&
+ cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
+ cec_ctrl->cec_device_suspend) {
+ DEV_DBG("%s: Sending power on at wakeup\n", __func__);
+ input_report_key(cec_ctrl->input, KEY_POWER, 1);
+ input_sync(cec_ctrl->input);
+ input_report_key(cec_ctrl->input, KEY_POWER, 0);
+ input_sync(cec_ctrl->input);
+ }
- if (cbs && cbs->msg_recv_notify)
- cbs->msg_recv_notify(cbs->data, &msg);
+ if ((msg.opcode == CEC_OP_STANDBY) &&
+ cec_ctrl->input && cec_ctrl->cec_wakeup_en &&
+ !cec_ctrl->cec_device_suspend) {
+ DEV_DBG("%s: Sending power off on standby\n", __func__);
+ input_report_key(cec_ctrl->input, KEY_POWER, 1);
+ input_sync(cec_ctrl->input);
+ input_report_key(cec_ctrl->input, KEY_POWER, 0);
+ input_sync(cec_ctrl->input);
+ }
+
+ if (cbs && cbs->msg_recv_notify)
+ cbs->msg_recv_notify(cbs->data, &msg);
+ }
}
/**