Loading drivers/video/fbdev/msm/mdss_cec_core.c +39 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -594,6 +594,41 @@ end: return ret; } static ssize_t cec_wta_clear_logical_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int clear_flag; unsigned long flags; ssize_t ret; struct cec_ctl *ctl = cec_get_ctl(dev); struct cec_ops *ops; if (!ctl) { pr_err("Invalid ctl\n"); ret = -EINVAL; goto end; } ops = ctl->init_data.ops; ret = kstrtoint(buf, 10, &clear_flag); if (ret) { pr_err("kstrtoint failed\n"); goto end; } ret = count; spin_lock_irqsave(&ctl->lock, flags); if (ctl->enabled) { if (ops && ops->clear_logical_addr) ops->clear_logical_addr(ops->data, !!clear_flag); } spin_unlock_irqrestore(&ctl->lock, flags); end: return ret; } static ssize_t cec_rda_msg(struct device *dev, struct device_attribute *attr, char *buf) { Loading Loading @@ -703,6 +738,8 @@ static DEVICE_ATTR(enable_compliance, S_IRUGO | S_IWUSR, cec_rda_enable_compliance, cec_wta_enable_compliance); static DEVICE_ATTR(logical_addr, S_IRUSR | S_IWUSR, cec_rda_logical_addr, cec_wta_logical_addr); static DEVICE_ATTR(clear_logical_addr, 0200, NULL, cec_wta_clear_logical_addr); static DEVICE_ATTR(rd_msg, S_IRUGO, cec_rda_msg, NULL); static DEVICE_ATTR(wr_msg, S_IWUSR | S_IRUSR, NULL, cec_wta_msg); Loading @@ -710,6 +747,7 @@ static struct attribute *cec_fs_attrs[] = { &dev_attr_enable.attr, &dev_attr_enable_compliance.attr, &dev_attr_logical_addr.attr, &dev_attr_clear_logical_addr.attr, &dev_attr_rd_msg.attr, &dev_attr_wr_msg.attr, NULL, Loading drivers/video/fbdev/msm/mdss_cec_core.h +2 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -67,6 +67,7 @@ struct cec_ops { int (*send_msg)(void *data, struct cec_msg *msg); void (*wt_logical_addr)(void *data, u8 addr); void (*clear_logical_addr)(void *data, bool flag); void (*wakeup_en)(void *data, bool en); bool (*is_wakeup_en)(void *data); void (*device_suspend)(void *data, bool suspend); Loading drivers/video/fbdev/msm/mdss_hdmi_cec.c +140 −56 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -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" Loading @@ -33,12 +34,19 @@ #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; bool cec_device_suspend; bool cec_clear_logical_addr; u32 cec_logical_addr; u32 cec_msg_wr_status; 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; Loading @@ -60,6 +68,20 @@ static int hdmi_cec_msg_send(void *data, struct cec_msg *msg) return -EINVAL; } if (msg->sender_id != cec_ctrl->cec_logical_addr && msg->recvr_id == 0xF) { /* * If the current logical address is not the * same as the sender_id and if the message is * broadcasting, the message is looping back. * Abort the message sending in that case */ DEV_ERR("%s: abort potential MAL msg %d:%d logical %d\n", __func__, msg->sender_id, msg->recvr_id, cec_ctrl->cec_logical_addr); return -EINVAL; } io = cec_ctrl->init_data.io; reinit_completion(&cec_ctrl->cec_msg_wr_done); Loading Loading @@ -164,65 +186,106 @@ static void hdmi_cec_deinit_input_event(struct hdmi_cec_ctrl *cec_ctrl) cec_ctrl->input = NULL; } static void hdmi_cec_msg_recv(struct work_struct *work) static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl) { int i; u32 data; struct hdmi_cec_ctrl *cec_ctrl = NULL; struct dss_io_data *io = NULL; struct cec_msg msg; struct cec_cbs *cbs; struct cec_msg *msg; u32 data; int i; u32 head; u32 tail; cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work); if (!cec_ctrl || !cec_ctrl->init_data.io) { DEV_ERR("%s: invalid input\n", __func__); return; return -EINVAL; } if (!cec_ctrl->cec_enabled) { DEV_ERR("%s: cec not enabled\n", __func__); return; return -ENODEV; } io = cec_ctrl->init_data.io; cbs = cec_ctrl->init_data.cbs; 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 */ } data = DSS_REG_R(io, HDMI_CEC_RD_DATA); msg = &cec_ctrl->recv_msg[head]; msg.recvr_id = (data & 0x000F); msg.sender_id = (data & 0x00F0) >> 4; msg.frame_size = (data & 0x1F00) >> 8; DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__, msg.sender_id, msg.recvr_id, msg.frame_size); io = cec_ctrl->init_data.io; data = DSS_REG_R(io, HDMI_CEC_RD_DATA); if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) { msg->recvr_id = (data & 0x000F); msg->sender_id = (data & 0x00F0) >> 4; msg->frame_size = (data & 0x1F00) >> 8; if (msg->frame_size < 1 || msg->frame_size > MAX_CEC_FRAME_SIZE) { DEV_ERR("%s: invalid message (frame length = %d)\n", __func__, msg.frame_size); return; } else if (msg.frame_size == 1) { __func__, msg->frame_size); return -EINVAL; } else if (msg->frame_size == 1) { DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n", __func__, msg.recvr_id, msg.sender_id); return; __func__, msg->recvr_id, msg->sender_id); return -EINVAL; } /* data block 0 : opcode */ data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); msg.opcode = data & 0xFF; msg->opcode = data & 0xFF; /* data block 1-14 : operand 0-13 */ for (i = 0; i < msg.frame_size - 2; i++) { for (i = 0; i < msg->frame_size - 2; i++) { data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); msg.operand[i] = data & 0xFF; msg->operand[i] = data & 0xFF; } for (; i < MAX_OPERAND_SIZE; i++) msg.operand[i] = 0; msg->operand[i] = 0; /* * Clearing the logical address is used when the system doesn't * need to process CEC command any more. */ 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, msg->opcode, cec_ctrl->cec_wakeup_en, cec_ctrl->cec_device_suspend); return 0; } 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) { DEV_ERR("%s: invalid input\n", __func__); return; } cbs = cec_ctrl->init_data.cbs; /* 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 && Loading @@ -247,6 +310,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work) if (cbs && cbs->msg_recv_notify) cbs->msg_recv_notify(cbs->data, &msg); } } /** * hdmi_cec_isr() - interrupt handler for cec hw module Loading Loading @@ -308,8 +372,12 @@ int hdmi_cec_isr(void *input) if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) { DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__); rc = hdmi_cec_msg_read(cec_ctrl); if (!rc) queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work); DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(6)); queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work); } return rc; Loading Loading @@ -360,8 +428,23 @@ static void hdmi_cec_write_logical_addr(void *input, u8 addr) return; } if (cec_ctrl->cec_enabled) if (cec_ctrl->cec_enabled) { DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF); cec_ctrl->cec_logical_addr = addr & 0xF; } } static void hdmi_cec_clear_logical_addr(void *input, bool clear_flag) { struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input; if (!cec_ctrl || !cec_ctrl->init_data.io) { DEV_ERR("%s: Invalid input\n", __func__); return; } if (cec_ctrl->cec_enabled) cec_ctrl->cec_clear_logical_addr = clear_flag; } static int hdmi_cec_enable(void *input, bool enable) Loading Loading @@ -474,6 +557,7 @@ void *hdmi_cec_init(struct hdmi_cec_init_data *init_data) /* populate hardware specific operations to client */ ops->send_msg = hdmi_cec_msg_send; ops->wt_logical_addr = hdmi_cec_write_logical_addr; ops->clear_logical_addr = hdmi_cec_clear_logical_addr; ops->enable = hdmi_cec_enable; ops->data = cec_ctrl; ops->wakeup_en = hdmi_cec_wakeup_en; Loading Loading
drivers/video/fbdev/msm/mdss_cec_core.c +39 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -594,6 +594,41 @@ end: return ret; } static ssize_t cec_wta_clear_logical_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int clear_flag; unsigned long flags; ssize_t ret; struct cec_ctl *ctl = cec_get_ctl(dev); struct cec_ops *ops; if (!ctl) { pr_err("Invalid ctl\n"); ret = -EINVAL; goto end; } ops = ctl->init_data.ops; ret = kstrtoint(buf, 10, &clear_flag); if (ret) { pr_err("kstrtoint failed\n"); goto end; } ret = count; spin_lock_irqsave(&ctl->lock, flags); if (ctl->enabled) { if (ops && ops->clear_logical_addr) ops->clear_logical_addr(ops->data, !!clear_flag); } spin_unlock_irqrestore(&ctl->lock, flags); end: return ret; } static ssize_t cec_rda_msg(struct device *dev, struct device_attribute *attr, char *buf) { Loading Loading @@ -703,6 +738,8 @@ static DEVICE_ATTR(enable_compliance, S_IRUGO | S_IWUSR, cec_rda_enable_compliance, cec_wta_enable_compliance); static DEVICE_ATTR(logical_addr, S_IRUSR | S_IWUSR, cec_rda_logical_addr, cec_wta_logical_addr); static DEVICE_ATTR(clear_logical_addr, 0200, NULL, cec_wta_clear_logical_addr); static DEVICE_ATTR(rd_msg, S_IRUGO, cec_rda_msg, NULL); static DEVICE_ATTR(wr_msg, S_IWUSR | S_IRUSR, NULL, cec_wta_msg); Loading @@ -710,6 +747,7 @@ static struct attribute *cec_fs_attrs[] = { &dev_attr_enable.attr, &dev_attr_enable_compliance.attr, &dev_attr_logical_addr.attr, &dev_attr_clear_logical_addr.attr, &dev_attr_rd_msg.attr, &dev_attr_wr_msg.attr, NULL, Loading
drivers/video/fbdev/msm/mdss_cec_core.h +2 −1 Original line number Diff line number Diff line /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -67,6 +67,7 @@ struct cec_ops { int (*send_msg)(void *data, struct cec_msg *msg); void (*wt_logical_addr)(void *data, u8 addr); void (*clear_logical_addr)(void *data, bool flag); void (*wakeup_en)(void *data, bool en); bool (*is_wakeup_en)(void *data); void (*device_suspend)(void *data, bool suspend); Loading
drivers/video/fbdev/msm/mdss_hdmi_cec.c +140 −56 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -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" Loading @@ -33,12 +34,19 @@ #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; bool cec_device_suspend; bool cec_clear_logical_addr; u32 cec_logical_addr; u32 cec_msg_wr_status; 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; Loading @@ -60,6 +68,20 @@ static int hdmi_cec_msg_send(void *data, struct cec_msg *msg) return -EINVAL; } if (msg->sender_id != cec_ctrl->cec_logical_addr && msg->recvr_id == 0xF) { /* * If the current logical address is not the * same as the sender_id and if the message is * broadcasting, the message is looping back. * Abort the message sending in that case */ DEV_ERR("%s: abort potential MAL msg %d:%d logical %d\n", __func__, msg->sender_id, msg->recvr_id, cec_ctrl->cec_logical_addr); return -EINVAL; } io = cec_ctrl->init_data.io; reinit_completion(&cec_ctrl->cec_msg_wr_done); Loading Loading @@ -164,65 +186,106 @@ static void hdmi_cec_deinit_input_event(struct hdmi_cec_ctrl *cec_ctrl) cec_ctrl->input = NULL; } static void hdmi_cec_msg_recv(struct work_struct *work) static int hdmi_cec_msg_read(struct hdmi_cec_ctrl *cec_ctrl) { int i; u32 data; struct hdmi_cec_ctrl *cec_ctrl = NULL; struct dss_io_data *io = NULL; struct cec_msg msg; struct cec_cbs *cbs; struct cec_msg *msg; u32 data; int i; u32 head; u32 tail; cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work); if (!cec_ctrl || !cec_ctrl->init_data.io) { DEV_ERR("%s: invalid input\n", __func__); return; return -EINVAL; } if (!cec_ctrl->cec_enabled) { DEV_ERR("%s: cec not enabled\n", __func__); return; return -ENODEV; } io = cec_ctrl->init_data.io; cbs = cec_ctrl->init_data.cbs; 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 */ } data = DSS_REG_R(io, HDMI_CEC_RD_DATA); msg = &cec_ctrl->recv_msg[head]; msg.recvr_id = (data & 0x000F); msg.sender_id = (data & 0x00F0) >> 4; msg.frame_size = (data & 0x1F00) >> 8; DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__, msg.sender_id, msg.recvr_id, msg.frame_size); io = cec_ctrl->init_data.io; data = DSS_REG_R(io, HDMI_CEC_RD_DATA); if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) { msg->recvr_id = (data & 0x000F); msg->sender_id = (data & 0x00F0) >> 4; msg->frame_size = (data & 0x1F00) >> 8; if (msg->frame_size < 1 || msg->frame_size > MAX_CEC_FRAME_SIZE) { DEV_ERR("%s: invalid message (frame length = %d)\n", __func__, msg.frame_size); return; } else if (msg.frame_size == 1) { __func__, msg->frame_size); return -EINVAL; } else if (msg->frame_size == 1) { DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n", __func__, msg.recvr_id, msg.sender_id); return; __func__, msg->recvr_id, msg->sender_id); return -EINVAL; } /* data block 0 : opcode */ data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); msg.opcode = data & 0xFF; msg->opcode = data & 0xFF; /* data block 1-14 : operand 0-13 */ for (i = 0; i < msg.frame_size - 2; i++) { for (i = 0; i < msg->frame_size - 2; i++) { data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA); msg.operand[i] = data & 0xFF; msg->operand[i] = data & 0xFF; } for (; i < MAX_OPERAND_SIZE; i++) msg.operand[i] = 0; msg->operand[i] = 0; /* * Clearing the logical address is used when the system doesn't * need to process CEC command any more. */ 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, msg->opcode, cec_ctrl->cec_wakeup_en, cec_ctrl->cec_device_suspend); return 0; } 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) { DEV_ERR("%s: invalid input\n", __func__); return; } cbs = cec_ctrl->init_data.cbs; /* 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 && Loading @@ -247,6 +310,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work) if (cbs && cbs->msg_recv_notify) cbs->msg_recv_notify(cbs->data, &msg); } } /** * hdmi_cec_isr() - interrupt handler for cec hw module Loading Loading @@ -308,8 +372,12 @@ int hdmi_cec_isr(void *input) if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) { DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__); rc = hdmi_cec_msg_read(cec_ctrl); if (!rc) queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work); DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(6)); queue_work(cec_ctrl->init_data.workq, &cec_ctrl->cec_read_work); } return rc; Loading Loading @@ -360,8 +428,23 @@ static void hdmi_cec_write_logical_addr(void *input, u8 addr) return; } if (cec_ctrl->cec_enabled) if (cec_ctrl->cec_enabled) { DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF); cec_ctrl->cec_logical_addr = addr & 0xF; } } static void hdmi_cec_clear_logical_addr(void *input, bool clear_flag) { struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input; if (!cec_ctrl || !cec_ctrl->init_data.io) { DEV_ERR("%s: Invalid input\n", __func__); return; } if (cec_ctrl->cec_enabled) cec_ctrl->cec_clear_logical_addr = clear_flag; } static int hdmi_cec_enable(void *input, bool enable) Loading Loading @@ -474,6 +557,7 @@ void *hdmi_cec_init(struct hdmi_cec_init_data *init_data) /* populate hardware specific operations to client */ ops->send_msg = hdmi_cec_msg_send; ops->wt_logical_addr = hdmi_cec_write_logical_addr; ops->clear_logical_addr = hdmi_cec_clear_logical_addr; ops->enable = hdmi_cec_enable; ops->data = cec_ctrl; ops->wakeup_en = hdmi_cec_wakeup_en; Loading