mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 22:14:04 +02:00
hdmi:cec:update driver to match android HDMI CEC HAL.
Android 5.x introduce HDMI CEC, so we need to porting cec hal and driver to make it work. Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
This commit is contained in:
parent
b2d4b94875
commit
06809f0306
|
|
@ -6,30 +6,18 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/firmware.h>
|
||||
#include "rockchip-hdmi-cec.h"
|
||||
#include "linux/ioctl.h"
|
||||
#include "linux/pagemap.h"
|
||||
|
||||
static struct cec_device *cec_dev;
|
||||
struct input_dev *devinput;
|
||||
static struct miscdevice mdev;
|
||||
|
||||
int key_table[] = {
|
||||
KEY_UP,
|
||||
KEY_DOWN,
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_REPLY,
|
||||
KEY_BACK,
|
||||
KEY_POWER,
|
||||
};
|
||||
|
||||
static void cecmenucontrol(int uitemp);
|
||||
|
||||
static int cecreadframe(struct cec_framedata *frame)
|
||||
{
|
||||
if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
|
||||
if (frame == NULL || !cec_dev ||
|
||||
cec_dev->readframe == NULL || !cec_dev->enable)
|
||||
return -1;
|
||||
else
|
||||
return cec_dev->readframe(cec_dev->hdmi, frame);
|
||||
|
|
@ -43,403 +31,32 @@ static int cecsendframe(struct cec_framedata *frame)
|
|||
return cec_dev->sendframe(cec_dev->hdmi, frame);
|
||||
}
|
||||
|
||||
static int cecsendping(char logicaddress)
|
||||
{
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
memset(&cecframe, 0, sizeof(struct cec_framedata));
|
||||
cecframe.srcdestaddr = logicaddress << 4 | logicaddress;
|
||||
return cec_dev->sendframe(cec_dev->hdmi, &cecframe);
|
||||
}
|
||||
|
||||
/*static int CecSendMessage (char opCode, char dest)
|
||||
{
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
cecframe.opcode = opCode;
|
||||
cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic, dest);
|
||||
cecframe.argcount = 0;
|
||||
|
||||
return cecsendframe(&cecframe);
|
||||
}*/
|
||||
|
||||
|
||||
/*static void CecSendFeatureAbort (struct cec_framedata *pcpi, char reason)
|
||||
{
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
if ((pcpi->srcdestaddr & 0x0F) != CEC_LOGADDR_UNREGORBC) {
|
||||
cecframe.opcode = CECOP_FEATURE_ABORT;
|
||||
cecframe.srcdestaddr = MAKE_SRCDEST( cec_dev->address_logic,
|
||||
( pcpi->srcdestaddr & 0xF0) >> 4 );
|
||||
cecframe.args[0] = pcpi->opcode;
|
||||
cecframe.args[1] = reason;
|
||||
cecframe.argcount = 2;
|
||||
cecsendframe(&cecframe);
|
||||
}
|
||||
}*/
|
||||
|
||||
static void cecsendimageview(void)
|
||||
{
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
cecframe.opcode = CECOP_IMAGE_VIEW_ON;
|
||||
cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_TV);
|
||||
cecframe.argcount = 0;
|
||||
cecsendframe(&cecframe);
|
||||
}
|
||||
|
||||
static void cecsendactivesource(void)
|
||||
{
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
cecframe.opcode = CECOP_ACTIVE_SOURCE;
|
||||
cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
cecframe.args[0] = (cec_dev->address_phy & 0xFF00) >> 8;
|
||||
cecframe.args[1] = (cec_dev->address_phy & 0x00FF);
|
||||
cecframe.argcount = 2;
|
||||
cecsendframe(&cecframe);
|
||||
}
|
||||
|
||||
static void cechandleinactivesource(struct cec_framedata *pcpi)
|
||||
{
|
||||
}
|
||||
|
||||
static void cechandlefeatureabort(struct cec_framedata *pcpi)
|
||||
{
|
||||
}
|
||||
|
||||
static bool validatececmessage(struct cec_framedata *pcpi)
|
||||
{
|
||||
char parametercount = 0;
|
||||
bool countok = true;
|
||||
|
||||
/* Determine required parameter count */
|
||||
|
||||
switch (pcpi->opcode) {
|
||||
case CECOP_IMAGE_VIEW_ON:
|
||||
case CECOP_TEXT_VIEW_ON:
|
||||
case CECOP_STANDBY:
|
||||
case CECOP_GIVE_PHYSICAL_ADDRESS:
|
||||
case CECOP_GIVE_DEVICE_POWER_STATUS:
|
||||
case CECOP_GET_MENU_LANGUAGE:
|
||||
case CECOP_GET_CEC_VERSION:
|
||||
parametercount = 0;
|
||||
break;
|
||||
case CECOP_REPORT_POWER_STATUS: /* power status*/
|
||||
case CECOP_CEC_VERSION: /* cec version*/
|
||||
parametercount = 1;
|
||||
break;
|
||||
case CECOP_INACTIVE_SOURCE: /* physical address*/
|
||||
case CECOP_FEATURE_ABORT:
|
||||
case CECOP_ACTIVE_SOURCE: /* physical address*/
|
||||
parametercount = 2;
|
||||
break;
|
||||
case CECOP_REPORT_PHYSICAL_ADDRESS:
|
||||
case CECOP_DEVICE_VENDOR_ID: /* vendor id*/
|
||||
parametercount = 3;
|
||||
break;
|
||||
case CECOP_SET_OSD_NAME: /* osd name (1-14 bytes)*/
|
||||
case CECOP_SET_OSD_STRING:
|
||||
parametercount = 1; /* must have a minimum of 1 operands*/
|
||||
break;
|
||||
case CECOP_ABORT:
|
||||
break;
|
||||
case CECOP_ARC_INITIATE:
|
||||
break;
|
||||
case CECOP_ARC_REPORT_INITIATED:
|
||||
break;
|
||||
case CECOP_ARC_REPORT_TERMINATED:
|
||||
break;
|
||||
case CECOP_ARC_REQUEST_INITIATION:
|
||||
break;
|
||||
case CECOP_ARC_REQUEST_TERMINATION:
|
||||
break;
|
||||
case CECOP_ARC_TERMINATE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test for correct parameter count. */
|
||||
|
||||
if (pcpi->argcount < parametercount)
|
||||
countok = false;
|
||||
|
||||
return countok;
|
||||
}
|
||||
|
||||
static bool cecrxmsghandlerlast(struct cec_framedata *pcpi)
|
||||
{
|
||||
bool isdirectaddressed;
|
||||
struct cec_framedata cecframe;
|
||||
|
||||
isdirectaddressed = !((pcpi->srcdestaddr & 0x0F) ==
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
pr_info("isDirectAddressed %d\n", (int)isdirectaddressed);
|
||||
if (validatececmessage(pcpi)) {
|
||||
/* If invalid message, ignore it, but treat it as handled */
|
||||
if (isdirectaddressed) {
|
||||
switch (pcpi->opcode) {
|
||||
case CECOP_USER_CONTROL_PRESSED:
|
||||
cecmenucontrol(pcpi->args[0]);
|
||||
break;
|
||||
|
||||
case CECOP_VENDOR_REMOTE_BUTTON_DOWN:
|
||||
cecmenucontrol(pcpi->args[0]);
|
||||
break;
|
||||
case CECOP_FEATURE_ABORT:
|
||||
cechandlefeatureabort(pcpi);
|
||||
break;
|
||||
|
||||
case CECOP_GIVE_OSD_NAME:
|
||||
cecframe.opcode = CECOP_SET_OSD_NAME;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_TV);
|
||||
cecframe.args[0] = 'r';
|
||||
cecframe.args[1] = 'k';
|
||||
cecframe.args[2] = '-';
|
||||
cecframe.args[3] = 'b';
|
||||
cecframe.args[4] = 'o';
|
||||
cecframe.args[5] = 'x';
|
||||
cecframe.argcount = 6;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_VENDOR_COMMAND_WITH_ID:
|
||||
|
||||
if (pcpi->args[2] == 00) {
|
||||
cecframe.opcode = CECOP_SET_OSD_NAME;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_TV);
|
||||
cecframe.args[0] = '1';
|
||||
cecframe.args[1] = '1';
|
||||
cecframe.args[2] = '1';
|
||||
cecframe.args[3] = '1';
|
||||
cecframe.args[4] = '1';
|
||||
cecframe.args[5] = '1';
|
||||
cecframe.argcount = 6;
|
||||
cecsendframe(&cecframe);
|
||||
}
|
||||
break;
|
||||
case CECOP_IMAGE_VIEW_ON:
|
||||
case CECOP_TEXT_VIEW_ON:
|
||||
/* In our case, respond the same to both these messages*/
|
||||
break;
|
||||
|
||||
case CECOP_GIVE_DEVICE_VENDOR_ID:
|
||||
cecframe.opcode = CECOP_DEVICE_VENDOR_ID;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
cecframe.args[0] = 0x1;
|
||||
cecframe.args[1] = 0x2;
|
||||
cecframe.args[2] = 0x3;
|
||||
cecframe.argcount = 3;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_STANDBY: /* Direct and Broadcast*/
|
||||
/* Setting this here will let the main task know */
|
||||
/* (via SI_CecGetPowerState) and at the same time */
|
||||
/* prevent us from broadcasting a STANDBY message */
|
||||
/* of our own when the main task responds by */
|
||||
/* calling SI_CecSetPowerState( STANDBY ); */
|
||||
cec_dev->powerstatus = CEC_POWERSTATUS_STANDBY;
|
||||
break;
|
||||
|
||||
case CECOP_INACTIVE_SOURCE:
|
||||
cechandleinactivesource(pcpi);
|
||||
break;
|
||||
|
||||
case CECOP_GIVE_PHYSICAL_ADDRESS:
|
||||
|
||||
cecframe.opcode = CECOP_REPORT_PHYSICAL_ADDRESS;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
cecframe.args[0] = (cec_dev->address_phy&0xFF00)>>8;
|
||||
cecframe.args[1] = (cec_dev->address_phy&0x00FF);
|
||||
cecframe.args[2] = cec_dev->address_logic;
|
||||
cecframe.argcount = 3;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_GIVE_DEVICE_POWER_STATUS:
|
||||
/* TV responds with power status. */
|
||||
|
||||
cecframe.opcode = CECOP_REPORT_POWER_STATUS;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
(pcpi->srcdestaddr & 0xF0) >> 4);
|
||||
cec_dev->powerstatus = 0x00;
|
||||
cecframe.args[0] = cec_dev->powerstatus;
|
||||
cecframe.argcount = 1;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_GET_MENU_LANGUAGE:
|
||||
/* TV Responds with a Set Menu language command. */
|
||||
|
||||
cecframe.opcode = CECOP_SET_MENU_LANGUAGE;
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
cecframe.args[0] = 'e';
|
||||
cecframe.args[1] = 'n';
|
||||
cecframe.args[2] = 'g';
|
||||
cecframe.argcount = 3;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_GET_CEC_VERSION:
|
||||
/* TV responds to this request with it's CEC version support.*/
|
||||
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_TV);
|
||||
cecframe.opcode = CECOP_CEC_VERSION;
|
||||
cecframe.args[0] = 0x05; /* Report CEC1.4b*/
|
||||
cecframe.argcount = 1;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
case CECOP_REPORT_POWER_STATUS:
|
||||
/*Someone sent us their power state.
|
||||
|
||||
l_sourcePowerStatus = pcpi->args[0];
|
||||
|
||||
let NEW SOURCE task know about it.
|
||||
|
||||
if ( l_cecTaskState.task == SI_CECTASK_NEWSOURCE )
|
||||
{
|
||||
l_cecTaskState.cpiState = CPI_RESPONSE;
|
||||
}*/
|
||||
break;
|
||||
|
||||
/* Do not reply to directly addressed 'Broadcast' msgs. */
|
||||
case CECOP_REQUEST_ACTIVE_SOURCE:
|
||||
cecsendactivesource();
|
||||
break;
|
||||
|
||||
case CECOP_ACTIVE_SOURCE:
|
||||
case CECOP_REPORT_PHYSICAL_ADDRESS:
|
||||
case CECOP_ROUTING_CHANGE:
|
||||
case CECOP_ROUTING_INFORMATION:
|
||||
case CECOP_SET_STREAM_PATH:
|
||||
case CECOP_SET_MENU_LANGUAGE:
|
||||
case CECOP_DEVICE_VENDOR_ID:
|
||||
break;
|
||||
|
||||
case CECOP_ABORT:
|
||||
break;
|
||||
default:
|
||||
/*CecSendFeatureAbort(pcpi, CECAR_UNRECOG_OPCODE);*/
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Respond to broadcast messages. */
|
||||
switch (pcpi->opcode) {
|
||||
case CECOP_STANDBY:
|
||||
/* Setting this here will let the main task know */
|
||||
/* (via SI_CecGetPowerState) and at the same time */
|
||||
/* prevent us from broadcasting a STANDBY message */
|
||||
/* of our own when the main task responds by */
|
||||
/* calling SI_CecSetPowerState( STANDBY ); */
|
||||
cec_dev->powerstatus = CEC_POWERSTATUS_STANDBY;
|
||||
input_event(devinput, EV_KEY, KEY_POWER, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_POWER, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
|
||||
case CECOP_ACTIVE_SOURCE:
|
||||
/*CecHandleActiveSource( pcpi );*/
|
||||
break;
|
||||
|
||||
case CECOP_REPORT_PHYSICAL_ADDRESS:
|
||||
/*CecHandleReportPhysicalAddress( pcpi );*/
|
||||
cecframe.srcdestaddr =
|
||||
MAKE_SRCDEST(cec_dev->address_logic,
|
||||
CEC_LOGADDR_UNREGORBC);
|
||||
cecframe.opcode = CECOP_CEC_VERSION;
|
||||
cecframe.args[0] = 0x05; /* CEC1.4b*/
|
||||
cecframe.argcount = 1;
|
||||
cecsendframe(&cecframe);
|
||||
break;
|
||||
|
||||
/* Do not reply to 'Broadcast' msgs that we don't need.*/
|
||||
case CECOP_REQUEST_ACTIVE_SOURCE:
|
||||
cecsendactivesource();
|
||||
break;
|
||||
case CECOP_ROUTING_CHANGE:
|
||||
case CECOP_ROUTING_INFORMATION:
|
||||
case CECOP_SET_STREAM_PATH:
|
||||
case CECOP_SET_MENU_LANGUAGE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cecenumeration(void)
|
||||
{
|
||||
char logicaddress[3] = {CEC_LOGADDR_PLAYBACK1,
|
||||
CEC_LOGADDR_PLAYBACK2,
|
||||
CEC_LOGADDR_PLAYBACK3};
|
||||
int i;
|
||||
int trynum;
|
||||
int rtvalue;
|
||||
int availablecnt;
|
||||
|
||||
if (!cec_dev)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
rtvalue = 0;
|
||||
availablecnt = 0;
|
||||
for (trynum = 0; trynum < 3; trynum++) {
|
||||
rtvalue = cecsendping(logicaddress[i]);
|
||||
if (rtvalue == 1) {
|
||||
availablecnt++;
|
||||
CECDBG("availablecnt: %d\n", availablecnt);
|
||||
}
|
||||
mdelay(5);
|
||||
}
|
||||
if (availablecnt > 1) {
|
||||
cec_dev->address_logic = logicaddress[i];
|
||||
CECDBG("logic address is 0x%x\n",
|
||||
cec_dev->address_logic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 3)
|
||||
cec_dev->address_logic = CEC_LOGADDR_UNREGORBC;
|
||||
cec_dev->setceclogicaddr(cec_dev->hdmi, cec_dev->address_logic);
|
||||
cecsendimageview();
|
||||
cecsendactivesource();
|
||||
}
|
||||
|
||||
static void cecworkfunc(struct work_struct *work)
|
||||
{
|
||||
struct cec_delayed_work *cec_w =
|
||||
container_of(work, struct cec_delayed_work, work.work);
|
||||
struct cec_framedata cecframe;
|
||||
struct cecframelist *list_node;
|
||||
|
||||
switch (cec_w->event) {
|
||||
case EVENT_ENUMERATE:
|
||||
cecenumeration();
|
||||
break;
|
||||
case EVENT_RX_FRAME:
|
||||
memset(&cecframe, 0, sizeof(struct cec_framedata));
|
||||
cecreadframe(&cecframe);
|
||||
cecrxmsghandlerlast(&cecframe);
|
||||
list_node = kmalloc(sizeof(*list_node), GFP_KERNEL);
|
||||
if (list_node == NULL) {
|
||||
pr_err("HDMI CEC: list kmalloc fail! ");
|
||||
return;
|
||||
}
|
||||
cecreadframe(&list_node->cecframe);
|
||||
if (cec_dev->enable) {
|
||||
mutex_lock(&cec_dev->cec_lock);
|
||||
list_add_tail(&(list_node->framelist),
|
||||
&cec_dev->ceclist);
|
||||
sysfs_notify(&cec_dev->device.this_device->kobj,
|
||||
NULL, "stat");
|
||||
mutex_unlock(&cec_dev->cec_lock);
|
||||
} else {
|
||||
kfree(list_node);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -471,112 +88,155 @@ void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
|
|||
|
||||
void rockchip_hdmi_cec_set_pa(int devpa)
|
||||
{
|
||||
if (cec_dev)
|
||||
struct list_head *pos, *n;
|
||||
|
||||
if (cec_dev) {
|
||||
cec_dev->address_phy = devpa;
|
||||
cecenumeration();
|
||||
}
|
||||
|
||||
static int cec_input_device_init(void)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
devinput = input_allocate_device();
|
||||
if (!devinput)
|
||||
return -ENOMEM;
|
||||
devinput->name = "hdmi_cec_key";
|
||||
/*devinput->dev.parent = &client->dev;*/
|
||||
devinput->phys = "hdmi_cec_key/input0";
|
||||
devinput->id.bustype = BUS_HOST;
|
||||
devinput->id.vendor = 0x0001;
|
||||
devinput->id.product = 0x0001;
|
||||
devinput->id.version = 0x0100;
|
||||
err = input_register_device(devinput);
|
||||
if (err < 0) {
|
||||
input_free_device(devinput);
|
||||
CECDBG("%s input device error", __func__);
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < (sizeof(key_table)/sizeof(int)); i++)
|
||||
input_set_capability(devinput, EV_KEY, key_table[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cecmenucontrol(int uitemp)
|
||||
{
|
||||
switch (uitemp) {
|
||||
case S_CEC_MAKESURE: /*make sure*/
|
||||
CECDBG("CEC UIcommand makesure\n");
|
||||
input_event(devinput, EV_KEY, KEY_REPLY, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_REPLY, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_UP: /*up*/
|
||||
CECDBG("CEC UIcommand up\n");
|
||||
input_event(devinput, EV_KEY, KEY_UP, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_UP, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_DOWN: /*down*/
|
||||
CECDBG("CEC UIcommand down\n");
|
||||
input_event(devinput, EV_KEY, KEY_DOWN, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_DOWN, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_LEFT: /*left*/
|
||||
CECDBG("CEC UIcommand left\n");
|
||||
input_event(devinput, EV_KEY, KEY_LEFT , 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_LEFT , 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_RIGHT: /*right*/
|
||||
CECDBG("CEC UIcommand right\n");
|
||||
input_event(devinput, EV_KEY, KEY_RIGHT, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_RIGHT, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_BACK: /*back*/
|
||||
CECDBG("CEC UIcommand back\n");
|
||||
input_event(devinput, EV_KEY, KEY_BACK, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_BACK, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
case S_CEC_VENDORBACK:
|
||||
CECDBG("CEC UIcommand vendor back\n");
|
||||
input_event(devinput, EV_KEY, KEY_BACK, 1);
|
||||
input_sync(devinput);
|
||||
input_event(devinput, EV_KEY, KEY_BACK, 0);
|
||||
input_sync(devinput);
|
||||
break;
|
||||
pr_info("%s %x\n", __func__, devpa);
|
||||
/*when hdmi hpd , ceclist will be reset*/
|
||||
mutex_lock(&cec_dev->cec_lock);
|
||||
if (!list_empty(&cec_dev->ceclist)) {
|
||||
list_for_each_safe(pos, n, &cec_dev->ceclist) {
|
||||
list_del(pos);
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
INIT_LIST_HEAD(&cec_dev->ceclist);
|
||||
sysfs_notify(&cec_dev->device.this_device->kobj, NULL, "stat");
|
||||
mutex_unlock(&cec_dev->cec_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ssize_t cec_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t cec_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", cec_dev->cecval);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", cec_dev->enable);
|
||||
}
|
||||
|
||||
static ssize_t cec_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
static ssize_t cec_enable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &(cec_dev->enable));
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t cec_phy_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%x\n", cec_dev->address_phy);
|
||||
}
|
||||
|
||||
static ssize_t cec_phy_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%s", cec_dev->cecval);
|
||||
return strnlen(buf, PAGE_SIZE);
|
||||
ret = kstrtoint(buf, 0, &(cec_dev->address_phy));
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute cec_control_attr = {
|
||||
.attr = {.name = "cec", .mode = 0666},
|
||||
.show = cec_show,
|
||||
.store = cec_store,
|
||||
static ssize_t cec_logic_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%02x\n", cec_dev->address_logic);
|
||||
}
|
||||
|
||||
static ssize_t cec_logic_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &(cec_dev->address_logic));
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t cec_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int stat;
|
||||
|
||||
mutex_lock(&cec_dev->cec_lock);
|
||||
if (!cec_dev->address_phy)
|
||||
stat = 0;
|
||||
else if (list_empty(&cec_dev->ceclist))
|
||||
stat = 1;
|
||||
else
|
||||
stat = 2;
|
||||
mutex_unlock(&cec_dev->cec_lock);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", stat);
|
||||
}
|
||||
|
||||
static struct device_attribute cec_attrs[] = {
|
||||
__ATTR(logic, 0666, cec_logic_show, cec_logic_store),
|
||||
__ATTR(phy, 0666, cec_phy_show, cec_phy_store),
|
||||
__ATTR(enable, 0666, cec_enable_show, cec_enable_store),
|
||||
__ATTR(stat, S_IRUGO, cec_state_show, NULL),
|
||||
};
|
||||
|
||||
static long cec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
void __user *argp;
|
||||
struct cec_framedata cecsendtemp;
|
||||
struct cecframelist *listemp;
|
||||
|
||||
argp = (void __user *)arg;
|
||||
switch (cmd) {
|
||||
case HDMI_IOCTL_CECSETLA:
|
||||
ret = copy_from_user(&cec_dev->address_logic,
|
||||
argp, sizeof(int));
|
||||
if (cec_dev->setceclogicaddr)
|
||||
cec_dev->setceclogicaddr(cec_dev->hdmi,
|
||||
cec_dev->address_logic);
|
||||
break;
|
||||
case HDMI_IOCTL_CECSEND:
|
||||
ret = copy_from_user(&cecsendtemp, argp,
|
||||
sizeof(struct cec_framedata));
|
||||
ret = cecsendframe(&cecsendtemp);
|
||||
cecsendtemp.returnval = ret;
|
||||
ret = copy_to_user(argp, &cecsendtemp,
|
||||
sizeof(struct cec_framedata));
|
||||
break;
|
||||
case HDMI_IOCTL_CECENAB:
|
||||
ret = copy_from_user(&cec_dev->enable, argp, sizeof(int));
|
||||
break;
|
||||
case HDMI_IOCTL_CECPHY:
|
||||
ret = copy_to_user(argp, &(cec_dev->address_phy), sizeof(int));
|
||||
break;
|
||||
case HDMI_IOCTL_CECLOGIC:
|
||||
ret = copy_to_user(argp, &(cec_dev->address_logic),
|
||||
sizeof(int));
|
||||
break;
|
||||
case HDMI_IOCTL_CECREAD:
|
||||
mutex_lock(&cec_dev->cec_lock);
|
||||
if (!list_empty(&cec_dev->ceclist)) {
|
||||
listemp = list_entry(cec_dev->ceclist.next,
|
||||
struct cecframelist, framelist);
|
||||
ret = copy_to_user(argp, &listemp->cecframe,
|
||||
sizeof(struct cec_framedata));
|
||||
list_del(&listemp->framelist);
|
||||
kfree(listemp);
|
||||
}
|
||||
mutex_unlock(&cec_dev->cec_lock);
|
||||
break;
|
||||
case HDMI_IOCTL_CECCLEARLA:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations cec_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.compat_ioctl = cec_ioctl,
|
||||
.unlocked_ioctl = cec_ioctl,
|
||||
};
|
||||
|
||||
int rockchip_hdmi_cec_init(struct hdmi *hdmi,
|
||||
|
|
@ -586,21 +246,18 @@ int rockchip_hdmi_cec_init(struct hdmi *hdmi,
|
|||
struct cec_framedata *),
|
||||
void (*setceclogicaddr)(struct hdmi *, int))
|
||||
{
|
||||
int ret;
|
||||
static int cecmicsdevflag = 1;
|
||||
int ret, i;
|
||||
|
||||
mdev.minor = MISC_DYNAMIC_MINOR;
|
||||
mdev.name = "cec";
|
||||
mdev.mode = 0666;
|
||||
cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
|
||||
if (!cec_dev) {
|
||||
pr_err("HDMI CEC: kmalloc fail!");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(cec_dev, 0, sizeof(struct cec_device));
|
||||
mutex_init(&cec_dev->cec_lock);
|
||||
INIT_LIST_HEAD(&cec_dev->ceclist);
|
||||
cec_dev->hdmi = hdmi;
|
||||
cec_dev->cecval[0] = '1';
|
||||
cec_dev->cecval[1] = '\0';
|
||||
cec_dev->enable = 1;
|
||||
cec_dev->sendframe = sendframe;
|
||||
cec_dev->readframe = readframe;
|
||||
cec_dev->setceclogicaddr = setceclogicaddr;
|
||||
|
|
@ -609,25 +266,26 @@ int rockchip_hdmi_cec_init(struct hdmi *hdmi,
|
|||
pr_err("HDMI CEC: create workqueue failed.\n");
|
||||
return -1;
|
||||
}
|
||||
if (cecmicsdevflag) {
|
||||
cec_input_device_init();
|
||||
if (misc_register(&mdev)) {
|
||||
cec_dev->device.minor = MISC_DYNAMIC_MINOR;
|
||||
cec_dev->device.name = "cec";
|
||||
cec_dev->device.mode = 0666;
|
||||
cec_dev->device.fops = &cec_fops;
|
||||
if (misc_register(&cec_dev->device)) {
|
||||
pr_err("CEC: Could not add cec misc driver\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = device_create_file(mdev.this_device, &cec_control_attr);
|
||||
if (ret) {
|
||||
pr_err("CEC: Could not add sys file enable\n");
|
||||
goto error1;
|
||||
}
|
||||
cecmicsdevflag = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(cec_attrs); i++) {
|
||||
ret = device_create_file(cec_dev->device.this_device,
|
||||
&cec_attrs[i]);
|
||||
if (ret) {
|
||||
pr_err("CEC: Could not add sys file\n");
|
||||
goto error1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
misc_deregister(&mdev);
|
||||
misc_deregister(&cec_dev->device);
|
||||
error:
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "rockchip-hdmi.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <linux/miscdevice.h>
|
||||
enum {
|
||||
CEC_LOGADDR_TV = 0x00,
|
||||
CEC_LOGADDR_RECDEV1 = 0x01,
|
||||
|
|
@ -133,7 +133,7 @@ struct cec_framedata {
|
|||
char opcode;
|
||||
char args[MAX_CMD_SIZE];
|
||||
char argcount;
|
||||
char nextframeargcount;
|
||||
char returnval;
|
||||
};
|
||||
|
||||
struct cec_delayed_work {
|
||||
|
|
@ -142,39 +142,44 @@ struct cec_delayed_work {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct cecframelist {
|
||||
struct list_head framelist;
|
||||
struct cec_framedata cecframe;
|
||||
};
|
||||
|
||||
struct cec_device {
|
||||
struct workqueue_struct *workqueue;
|
||||
struct hdmi *hdmi;
|
||||
struct miscdevice device;
|
||||
int address_phy;
|
||||
int address_logic;
|
||||
int powerstatus;
|
||||
char cecval[32];
|
||||
int enable;
|
||||
struct list_head ceclist;
|
||||
struct mutex cec_lock; /* mutex for hdmicec operation*/
|
||||
|
||||
int (*sendframe)(struct hdmi *, struct cec_framedata *);
|
||||
int (*readframe)(struct hdmi *, struct cec_framedata *);
|
||||
void (*setceclogicaddr)(struct hdmi *, int);
|
||||
};
|
||||
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#define CECDBG(format, ...) \
|
||||
pr_info(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define CECDBG(format, ...)
|
||||
#endif
|
||||
|
||||
/*====================================
|
||||
//used for cec key control direction OK and back
|
||||
====================================*/
|
||||
enum {
|
||||
S_CEC_MAKESURE = 0x0,
|
||||
S_CEC_UP = 0x1,
|
||||
S_CEC_DOWN = 0x2,
|
||||
S_CEC_LEFT = 0x3,
|
||||
S_CEC_RIGHT = 0x4,
|
||||
S_CEC_BACK = 0x0d,
|
||||
S_CEC_VENDORBACK = 0x91,
|
||||
};
|
||||
|
||||
/* for HAL ioctl*/
|
||||
#define HDMI_CEC_MAGIC 'N'
|
||||
#define HDMI_IOCTL_CECSEND _IOW(HDMI_CEC_MAGIC, 0, struct cec_framedata)
|
||||
#define HDMI_IOCTL_CECENAB _IOW(HDMI_CEC_MAGIC, 1, int)
|
||||
#define HDMI_IOCTL_CECPHY _IOR(HDMI_CEC_MAGIC, 2, int)
|
||||
#define HDMI_IOCTL_CECLOGIC _IOR(HDMI_CEC_MAGIC, 3, int)
|
||||
#define HDMI_IOCTL_CECREAD _IOR(HDMI_CEC_MAGIC, 4, struct cec_framedata)
|
||||
#define HDMI_IOCTL_CECSETLA _IOW(HDMI_CEC_MAGIC, 5, int)
|
||||
#define HDMI_IOCTL_CECCLEARLA _IOW(HDMI_CEC_MAGIC, 6, int)
|
||||
/*for HAL ioctl end*/
|
||||
int rockchip_hdmi_cec_init(struct hdmi *hdmi,
|
||||
int (*sendframe)(struct hdmi *,
|
||||
struct cec_framedata *),
|
||||
|
|
|
|||
|
|
@ -255,6 +255,8 @@ static void hdmi_wq_remove(struct hdmi *hdmi)
|
|||
DBG("%s\n", __func__);
|
||||
if (hdmi->ops->remove)
|
||||
hdmi->ops->remove(hdmi);
|
||||
if (hdmi->property->feature & SUPPORT_CEC)
|
||||
rockchip_hdmi_cec_set_pa(0);
|
||||
if (hdmi->hotplug == HDMI_HPD_ACTIVED) {
|
||||
screen.type = SCREEN_HDMI;
|
||||
rk_fb_switch_screen(&screen, 0, hdmi->lcdc->id);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ static int rockchip_hdmiv2_cec_readframe(struct hdmi *hdmi,
|
|||
data[i] = hdmi_readl(hdmi_dev, CEC_RX_DATA0 + i);
|
||||
CECDBG("%02x\n", data[i]);
|
||||
}
|
||||
frame->argcount = count - 2;
|
||||
hdmi_writel(hdmi_dev, CEC_LOCK, 0x0);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -51,10 +52,8 @@ static int rockchip_hdmiv2_cec_sendframe(struct hdmi *hdmi,
|
|||
{
|
||||
struct hdmi_dev *hdmi_dev = hdmi->property->priv;
|
||||
int i, interrupt;
|
||||
int retrycnt = 0;
|
||||
int pingretry = 0, pingflg = 0;
|
||||
|
||||
CECDBG("TX srcDestAddr %02x opcode %02x ",
|
||||
CECDBG("TX srcdestaddr %02x opcode %02x ",
|
||||
frame->srcdestaddr, frame->opcode);
|
||||
if (frame->argcount) {
|
||||
CECDBG("args:");
|
||||
|
|
@ -62,76 +61,34 @@ static int rockchip_hdmiv2_cec_sendframe(struct hdmi *hdmi,
|
|||
CECDBG("%02x ", frame->args[i]);
|
||||
}
|
||||
CECDBG("\n");
|
||||
while (retrycnt < 3) {
|
||||
if (retrycnt) {
|
||||
hdmi_msk_reg(hdmi_dev, CEC_CTRL, m_CEC_FRAME_TYPE,
|
||||
v_CEC_FRAME_TYPE(0));
|
||||
}
|
||||
CECDBG("reTryCnt: %d\n", retrycnt);
|
||||
|
||||
if ((frame->srcdestaddr & 0x0f) ==
|
||||
((frame->srcdestaddr >> 4) & 0x0f)) {
|
||||
/*it is a ping command*/
|
||||
pingflg = 1;
|
||||
if (pingretry >= 3)
|
||||
break;
|
||||
CECDBG("PingRetry: %d\n", pingretry);
|
||||
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_CNT, 1);
|
||||
} else {
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0 + 1, frame->opcode);
|
||||
for (i = 0; i < frame->argcount; i++)
|
||||
hdmi_writel(hdmi_dev,
|
||||
CEC_TX_DATA0 + 2 + i,
|
||||
frame->args[i]);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_CNT,
|
||||
frame->argcount + 2);
|
||||
}
|
||||
/*Start TX*/
|
||||
hdmi_msk_reg(hdmi_dev, CEC_CTRL, m_CEC_SEND, v_CEC_SEND(1));
|
||||
i = 60;
|
||||
while (i--) {
|
||||
usleep_range(900, 1000);
|
||||
interrupt = hdmi_readl(hdmi_dev, IH_CEC_STAT0);
|
||||
if (interrupt & (m_ERR_INITIATOR | m_ARB_LOST |
|
||||
m_NACK | m_DONE)) {
|
||||
hdmi_writel(hdmi_dev, IH_CEC_STAT0,
|
||||
interrupt & (m_ERR_INITIATOR |
|
||||
m_ARB_LOST | m_NACK | m_DONE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((interrupt & m_DONE)) {
|
||||
if (retrycnt > 1)
|
||||
hdmi_msk_reg(hdmi_dev, CEC_CTRL,
|
||||
m_CEC_FRAME_TYPE,
|
||||
v_CEC_FRAME_TYPE(2));
|
||||
if ((frame->srcdestaddr & 0x0f) == ((frame->srcdestaddr >> 4) & 0x0f)) {
|
||||
/*it is a ping command*/
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_CNT, 1);
|
||||
} else {
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0, frame->srcdestaddr);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_DATA0 + 1, frame->opcode);
|
||||
for (i = 0; i < frame->argcount; i++)
|
||||
hdmi_writel(hdmi_dev,
|
||||
CEC_TX_DATA0 + 2 + i, frame->args[i]);
|
||||
hdmi_writel(hdmi_dev, CEC_TX_CNT, frame->argcount + 2);
|
||||
}
|
||||
/*Start TX*/
|
||||
hdmi_msk_reg(hdmi_dev, CEC_CTRL, m_CEC_SEND, v_CEC_SEND(1));
|
||||
i = 400;
|
||||
/* time = 2.4(ms)*(1 + 16)(head + param)*11(bit)*/
|
||||
/*11bit = start bit(4.5ms) + data bit(2.4ms) */
|
||||
while (i--) {
|
||||
usleep_range(900, 1000);
|
||||
interrupt = hdmi_readl(hdmi_dev, IH_CEC_STAT0);
|
||||
if (interrupt & (m_ERR_INITIATOR | m_ARB_LOST |
|
||||
m_NACK | m_DONE)) {
|
||||
hdmi_writel(hdmi_dev, IH_CEC_STAT0,
|
||||
interrupt & (m_ERR_INITIATOR |
|
||||
m_ARB_LOST | m_NACK | m_DONE));
|
||||
break;
|
||||
} else {
|
||||
retrycnt++;
|
||||
}
|
||||
|
||||
if (pingflg == 1) {
|
||||
if (!(interrupt & (m_DONE | m_NACK))) {
|
||||
pingretry++;
|
||||
} else {
|
||||
/*got ack or no nack, finish ping retry action*/
|
||||
if (retrycnt > 1)
|
||||
hdmi_msk_reg(hdmi_dev, CEC_CTRL,
|
||||
m_CEC_FRAME_TYPE,
|
||||
v_CEC_FRAME_TYPE(2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retrycnt >= 3)
|
||||
CECDBG("send cec frame retry timeout !\n");
|
||||
if (pingretry >= 3)
|
||||
CECDBG("send cec frame pingretry timeout !\n");
|
||||
CECDBG("%s interrupt 0x%02x\n", __func__, interrupt);
|
||||
if (interrupt & m_DONE)
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user