2024-09-09 08:57:42 +00:00
|
|
|
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
|
2024-09-09 08:52:07 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* only version 2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/types.h>
|
2024-09-09 08:57:42 +00:00
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/device.h>
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
#include "mdss_hdmi_cec.h"
|
2024-09-09 08:57:42 +00:00
|
|
|
#include "mdss_panel.h"
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
#define CEC_STATUS_WR_ERROR BIT(0)
|
|
|
|
#define CEC_STATUS_WR_DONE BIT(1)
|
2024-09-09 08:57:42 +00:00
|
|
|
#define CEC_INTR (BIT(1) | BIT(3) | BIT(7))
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
#define CEC_SUPPORTED_HW_VERSION 0x30000001
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* Reference: HDMI 1.4a Specification section 7.1 */
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
struct hdmi_cec_ctrl {
|
|
|
|
bool cec_enabled;
|
|
|
|
|
|
|
|
u32 cec_msg_wr_status;
|
|
|
|
spinlock_t lock;
|
|
|
|
struct work_struct cec_read_work;
|
|
|
|
struct completion cec_msg_wr_done;
|
|
|
|
struct hdmi_cec_init_data init_data;
|
|
|
|
};
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
static int hdmi_cec_msg_send(void *data, struct cec_msg *msg)
|
2024-09-09 08:52:07 +00:00
|
|
|
{
|
2024-09-09 08:57:42 +00:00
|
|
|
int i, line_check_retry = 10, rc = 0;
|
2024-09-09 08:52:07 +00:00
|
|
|
u32 frame_retransmit = RETRANSMIT_MAX_NUM;
|
|
|
|
bool frame_type;
|
|
|
|
unsigned long flags;
|
|
|
|
struct dss_io_data *io = NULL;
|
2024-09-09 08:57:42 +00:00
|
|
|
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)data;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
if (!cec_ctrl || !cec_ctrl->init_data.io || !msg) {
|
|
|
|
DEV_ERR("%s: Invalid input\n", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
io = cec_ctrl->init_data.io;
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
reinit_completion(&cec_ctrl->cec_msg_wr_done);
|
2024-09-09 08:52:07 +00:00
|
|
|
cec_ctrl->cec_msg_wr_status = 0;
|
|
|
|
frame_type = (msg->recvr_id == 15 ? BIT(0) : 0);
|
|
|
|
if (msg->retransmit > 0 && msg->retransmit < RETRANSMIT_MAX_NUM)
|
|
|
|
frame_retransmit = msg->retransmit;
|
|
|
|
|
|
|
|
/* toggle cec in order to flush out bad hw state, if any */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_CTRL, 0);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
|
|
|
|
|
|
|
|
frame_retransmit = (frame_retransmit & 0xF) << 4;
|
|
|
|
DSS_REG_W(io, HDMI_CEC_RETRANSMIT, BIT(0) | frame_retransmit);
|
|
|
|
|
|
|
|
/* header block */
|
|
|
|
DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
|
|
|
|
(((msg->sender_id << 4) | msg->recvr_id) << 8) | frame_type);
|
|
|
|
|
|
|
|
/* data block 0 : opcode */
|
|
|
|
DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
|
|
|
|
((msg->frame_size < 2 ? 0 : msg->opcode) << 8) | frame_type);
|
|
|
|
|
|
|
|
/* data block 1-14 : operand 0-13 */
|
|
|
|
for (i = 0; i < msg->frame_size - 2; i++)
|
|
|
|
DSS_REG_W_ND(io, HDMI_CEC_WR_DATA,
|
|
|
|
(msg->operand[i] << 8) | frame_type);
|
|
|
|
|
|
|
|
while ((DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0)) &&
|
2024-09-09 08:57:42 +00:00
|
|
|
line_check_retry) {
|
|
|
|
line_check_retry--;
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_DBG("%s: CEC line is busy(%d)\n", __func__,
|
|
|
|
line_check_retry);
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!line_check_retry && (DSS_REG_R(io, HDMI_CEC_STATUS) & BIT(0))) {
|
|
|
|
DEV_ERR("%s: CEC line is busy. Retry\n", __func__);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start transmission */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0) | BIT(1) |
|
|
|
|
((msg->frame_size & 0x1F) << 4) | BIT(9));
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (!wait_for_completion_timeout(
|
2024-09-09 08:52:07 +00:00
|
|
|
&cec_ctrl->cec_msg_wr_done, HZ)) {
|
|
|
|
DEV_ERR("%s: timedout", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&cec_ctrl->lock, flags);
|
2024-09-09 08:57:42 +00:00
|
|
|
if (cec_ctrl->cec_msg_wr_status == CEC_STATUS_WR_ERROR) {
|
|
|
|
rc = -ENXIO;
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_ERR("%s: msg write failed.\n", __func__);
|
2024-09-09 08:57:42 +00:00
|
|
|
} else {
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_DBG("%s: CEC write frame done (frame len=%d)", __func__,
|
|
|
|
msg->frame_size);
|
2024-09-09 08:57:42 +00:00
|
|
|
}
|
2024-09-09 08:52:07 +00:00
|
|
|
spin_unlock_irqrestore(&cec_ctrl->lock, flags);
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
return rc;
|
2024-09-09 08:52:07 +00:00
|
|
|
} /* hdmi_cec_msg_send */
|
|
|
|
|
|
|
|
static void hdmi_cec_msg_recv(struct work_struct *work)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 data;
|
|
|
|
struct hdmi_cec_ctrl *cec_ctrl = NULL;
|
|
|
|
struct dss_io_data *io = NULL;
|
2024-09-09 08:57:42 +00:00
|
|
|
struct cec_msg msg;
|
|
|
|
struct cec_cbs *cbs;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
cec_ctrl = container_of(work, struct hdmi_cec_ctrl, cec_read_work);
|
2024-09-09 08:57:42 +00:00
|
|
|
if (!cec_ctrl || !cec_ctrl->init_data.io) {
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_ERR("%s: invalid input\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (!cec_ctrl->cec_enabled) {
|
|
|
|
DEV_ERR("%s: cec not enabled\n", __func__);
|
2024-09-09 08:52:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
io = cec_ctrl->init_data.io;
|
|
|
|
cbs = cec_ctrl->init_data.cbs;
|
|
|
|
|
2024-09-09 08:52:07 +00:00
|
|
|
data = DSS_REG_R(io, HDMI_CEC_RD_DATA);
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
msg.recvr_id = (data & 0x000F);
|
|
|
|
msg.sender_id = (data & 0x00F0) >> 4;
|
|
|
|
msg.frame_size = (data & 0x1F00) >> 8;
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_DBG("%s: Recvd init=[%u] dest=[%u] size=[%u]\n", __func__,
|
2024-09-09 08:57:42 +00:00
|
|
|
msg.sender_id, msg.recvr_id,
|
|
|
|
msg.frame_size);
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (msg.frame_size < 1) {
|
|
|
|
DEV_ERR("%s: invalid message (frame length = %d)\n",
|
|
|
|
__func__, msg.frame_size);
|
2024-09-09 08:52:07 +00:00
|
|
|
return;
|
2024-09-09 08:57:42 +00:00
|
|
|
} else if (msg.frame_size == 1) {
|
|
|
|
DEV_DBG("%s: polling message (dest[%x] <- init[%x])\n",
|
|
|
|
__func__, msg.recvr_id, msg.sender_id);
|
2024-09-09 08:52:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data block 0 : opcode */
|
|
|
|
data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
|
2024-09-09 08:57:42 +00:00
|
|
|
msg.opcode = data & 0xFF;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
/* data block 1-14 : operand 0-13 */
|
2024-09-09 08:57:42 +00:00
|
|
|
for (i = 0; i < msg.frame_size - 2; i++) {
|
2024-09-09 08:52:07 +00:00
|
|
|
data = DSS_REG_R_ND(io, HDMI_CEC_RD_DATA);
|
2024-09-09 08:57:42 +00:00
|
|
|
msg.operand[i] = data & 0xFF;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (; i < 14; i++)
|
2024-09-09 08:57:42 +00:00
|
|
|
msg.operand[i] = 0;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (cbs && cbs->msg_recv_notify)
|
|
|
|
cbs->msg_recv_notify(cbs->data, &msg);
|
|
|
|
}
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/**
|
|
|
|
* hdmi_cec_isr() - interrupt handler for cec hw module
|
|
|
|
* @cec_ctrl: pointer to cec hw module's data
|
|
|
|
*
|
|
|
|
* Return: irq error code
|
|
|
|
*
|
|
|
|
* The API can be called by HDMI Tx driver on receiving hw interrupts
|
|
|
|
* to let the CEC related interrupts handled by this module.
|
|
|
|
*/
|
2024-09-09 08:52:07 +00:00
|
|
|
int hdmi_cec_isr(void *input)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
u32 cec_intr, cec_status;
|
|
|
|
unsigned long flags;
|
|
|
|
struct dss_io_data *io = NULL;
|
|
|
|
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 -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
io = cec_ctrl->init_data.io;
|
|
|
|
|
|
|
|
cec_intr = DSS_REG_R_ND(io, HDMI_CEC_INT);
|
|
|
|
|
|
|
|
if (!cec_ctrl->cec_enabled) {
|
|
|
|
DSS_REG_W(io, HDMI_CEC_INT, cec_intr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cec_status = DSS_REG_R_ND(io, HDMI_CEC_STATUS);
|
|
|
|
|
|
|
|
if ((cec_intr & BIT(0)) && (cec_intr & BIT(1))) {
|
|
|
|
DEV_DBG("%s: CEC_IRQ_FRAME_WR_DONE\n", __func__);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(0));
|
|
|
|
|
|
|
|
spin_lock_irqsave(&cec_ctrl->lock, flags);
|
|
|
|
cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_DONE;
|
|
|
|
spin_unlock_irqrestore(&cec_ctrl->lock, flags);
|
|
|
|
|
|
|
|
if (!completion_done(&cec_ctrl->cec_msg_wr_done))
|
|
|
|
complete_all(&cec_ctrl->cec_msg_wr_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((cec_intr & BIT(2)) && (cec_intr & BIT(3))) {
|
|
|
|
DEV_DBG("%s: CEC_IRQ_FRAME_ERROR\n", __func__);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_INT, cec_intr | BIT(2));
|
|
|
|
|
|
|
|
spin_lock_irqsave(&cec_ctrl->lock, flags);
|
|
|
|
cec_ctrl->cec_msg_wr_status |= CEC_STATUS_WR_ERROR;
|
|
|
|
spin_unlock_irqrestore(&cec_ctrl->lock, flags);
|
|
|
|
|
|
|
|
if (!completion_done(&cec_ctrl->cec_msg_wr_done))
|
|
|
|
complete_all(&cec_ctrl->cec_msg_wr_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((cec_intr & BIT(6)) && (cec_intr & BIT(7))) {
|
|
|
|
DEV_DBG("%s: CEC_IRQ_FRAME_RD_DONE\n", __func__);
|
|
|
|
|
|
|
|
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;
|
2024-09-09 08:57:42 +00:00
|
|
|
}
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
static void hdmi_cec_write_logical_addr(void *input, u8 addr)
|
2024-09-09 08:52:07 +00:00
|
|
|
{
|
|
|
|
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (!cec_ctrl || !cec_ctrl->init_data.io) {
|
2024-09-09 08:52:07 +00:00
|
|
|
DEV_ERR("%s: Invalid input\n", __func__);
|
2024-09-09 08:57:42 +00:00
|
|
|
return;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
if (cec_ctrl->cec_enabled)
|
|
|
|
DSS_REG_W(cec_ctrl->init_data.io, HDMI_CEC_ADDR, addr & 0xF);
|
|
|
|
}
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
static int hdmi_cec_enable(void *input, bool enable)
|
2024-09-09 08:52:07 +00:00
|
|
|
{
|
2024-09-09 08:57:42 +00:00
|
|
|
int ret = 0;
|
|
|
|
u32 hdmi_hw_version, reg_val;
|
2024-09-09 08:52:07 +00:00
|
|
|
struct dss_io_data *io = NULL;
|
|
|
|
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
|
2024-09-09 08:57:42 +00:00
|
|
|
struct mdss_panel_info *pinfo;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
if (!cec_ctrl || !cec_ctrl->init_data.io) {
|
|
|
|
DEV_ERR("%s: Invalid input\n", __func__);
|
2024-09-09 08:57:42 +00:00
|
|
|
ret = -EPERM;
|
|
|
|
goto end;
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
io = cec_ctrl->init_data.io;
|
2024-09-09 08:57:42 +00:00
|
|
|
pinfo = cec_ctrl->init_data.pinfo;
|
|
|
|
|
|
|
|
if (!pinfo) {
|
|
|
|
DEV_ERR("%s: invalid pinfo\n", __func__);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
/* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16));
|
|
|
|
|
|
|
|
hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION);
|
|
|
|
if (hdmi_hw_version >= CEC_SUPPORTED_HW_VERSION) {
|
|
|
|
DSS_REG_W(io, HDMI_CEC_RD_RANGE, 0x30AB9888);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_WR_RANGE, 0x888AA888);
|
|
|
|
|
|
|
|
DSS_REG_W(io, HDMI_CEC_RD_START_RANGE, 0x88888888);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_RD_TOTAL_RANGE, 0x99);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_COMPL_CTL, 0xF);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_WR_CHECK_CONFIG, 0x4);
|
|
|
|
} else {
|
|
|
|
DEV_DBG("%s: CEC version %d is not supported.\n",
|
|
|
|
__func__, hdmi_hw_version);
|
|
|
|
ret = -EPERM;
|
|
|
|
goto end;
|
|
|
|
}
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
DSS_REG_W(io, HDMI_CEC_RD_FILTER, BIT(0) | (0x7FF << 4));
|
|
|
|
DSS_REG_W(io, HDMI_CEC_TIME, BIT(0) | ((7 * 0x30) << 7));
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* Enable CEC interrupts */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_INT, CEC_INTR);
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* Enable Engine */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_CTRL, BIT(0));
|
2024-09-09 08:52:07 +00:00
|
|
|
} else {
|
2024-09-09 08:57:42 +00:00
|
|
|
/* Disable Engine */
|
|
|
|
DSS_REG_W(io, HDMI_CEC_CTRL, 0);
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* Disable CEC interrupts */
|
|
|
|
reg_val = DSS_REG_R(io, HDMI_CEC_INT);
|
|
|
|
DSS_REG_W(io, HDMI_CEC_INT, reg_val & ~CEC_INTR);
|
2024-09-09 08:52:07 +00:00
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
cec_ctrl->cec_enabled = enable;
|
|
|
|
end:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hdmi_cec_init() - Initialize the CEC hw module
|
|
|
|
* @init_data: data needed to initialize the cec hw module
|
|
|
|
*
|
|
|
|
* Return: pointer to cec hw modules data that needs to be passed when
|
|
|
|
* calling cec hw modules API or error code.
|
|
|
|
*
|
|
|
|
* The API registers CEC HW modules with the client and provides HW
|
|
|
|
* specific operations.
|
|
|
|
*/
|
2024-09-09 08:52:07 +00:00
|
|
|
void *hdmi_cec_init(struct hdmi_cec_init_data *init_data)
|
|
|
|
{
|
2024-09-09 08:57:42 +00:00
|
|
|
struct hdmi_cec_ctrl *cec_ctrl;
|
|
|
|
struct cec_ops *ops;
|
|
|
|
int ret = 0;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
|
|
|
if (!init_data) {
|
|
|
|
DEV_ERR("%s: Invalid input\n", __func__);
|
2024-09-09 08:57:42 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ops = init_data->ops;
|
|
|
|
if (!ops) {
|
|
|
|
DEV_ERR("%s: no ops provided\n", __func__);
|
|
|
|
ret = -EINVAL;
|
2024-09-09 08:52:07 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cec_ctrl = kzalloc(sizeof(*cec_ctrl), GFP_KERNEL);
|
|
|
|
if (!cec_ctrl) {
|
|
|
|
DEV_ERR("%s: FAILED: out of memory\n", __func__);
|
2024-09-09 08:57:42 +00:00
|
|
|
ret = -EINVAL;
|
2024-09-09 08:52:07 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* keep a copy of init data */
|
2024-09-09 08:52:07 +00:00
|
|
|
cec_ctrl->init_data = *init_data;
|
|
|
|
|
|
|
|
spin_lock_init(&cec_ctrl->lock);
|
|
|
|
INIT_WORK(&cec_ctrl->cec_read_work, hdmi_cec_msg_recv);
|
|
|
|
init_completion(&cec_ctrl->cec_msg_wr_done);
|
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
/* populate hardware specific operations to client */
|
|
|
|
ops->send_msg = hdmi_cec_msg_send;
|
|
|
|
ops->wt_logical_addr = hdmi_cec_write_logical_addr;
|
|
|
|
ops->enable = hdmi_cec_enable;
|
|
|
|
ops->data = cec_ctrl;
|
2024-09-09 08:52:07 +00:00
|
|
|
|
2024-09-09 08:57:42 +00:00
|
|
|
return cec_ctrl;
|
2024-09-09 08:52:07 +00:00
|
|
|
error:
|
2024-09-09 08:57:42 +00:00
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hdmi_cec_deinit() - de-initialize CEC HW module
|
|
|
|
* @data: CEC HW module data
|
|
|
|
*
|
|
|
|
* This API release all resources allocated.
|
|
|
|
*/
|
|
|
|
void hdmi_cec_deinit(void *data)
|
|
|
|
{
|
|
|
|
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)data;
|
|
|
|
|
2024-09-09 08:52:07 +00:00
|
|
|
kfree(cec_ctrl);
|
2024-09-09 08:57:42 +00:00
|
|
|
}
|