12ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel/*
22ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
32ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*
42ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* Redistribution and use in source and binary forms, with or without
52ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* modification, are permitted provided that the following conditions are
62ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* met:
72ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*    * Redistributions of source code must retain the above copyright
82ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      notice, this list of conditions and the following disclaimer.
92ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*    * Redistributions in binary form must reproduce the above
102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      copyright notice, this list of conditions and the following
112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      disclaimer in the documentation and/or other materials provided
122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      with the distribution.
132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*    * Neither the name of The Linux Foundation. nor the names of its
142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      contributors may be used to endorse or promote products derived
152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*      from this software without specific prior written permission.
162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*
172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
192ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
202ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
212ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
222ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
232ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
242ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
252ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
262ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
272ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel*/
292ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
302ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#define DEBUG 0
312ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
322ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <cstdlib>
332ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <cutils/log.h>
342ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <errno.h>
352ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <fcntl.h>
362ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <hardware/hdmi_cec.h>
372ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include <utils/Trace.h>
382ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include "qhdmi_cec.h"
392ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel#include "QHDMIClient.h"
402ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
412ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelnamespace qhdmicec {
422ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
432ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelconst int NUM_HDMI_PORTS = 1;
442ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelconst int MAX_SYSFS_DATA = 128;
452ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelconst int MAX_CEC_FRAME_SIZE = 20;
462ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelconst int MAX_SEND_MESSAGE_RETRIES = 1;
472ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
482ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelenum {
492ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    LOGICAL_ADDRESS_SET   =  1,
502ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    LOGICAL_ADDRESS_UNSET = -1,
512ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel};
522ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
532ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel// Offsets of members of struct hdmi_cec_msg
542ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel// drivers/video/msm/mdss/mdss_hdmi_cec.c
552ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel// XXX: Get this from a driver header
562ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelenum {
572ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_SENDER_ID,
582ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_RECEIVER_ID,
592ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_OPCODE,
602ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_OPERAND,
612ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_FRAME_LENGTH = 17,
622ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    CEC_OFFSET_RETRANSMIT,
632ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel};
642ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
652ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel//Forward declarations
662ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_close_context(cec_context_t* ctx __unused);
672ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_enable(cec_context_t *ctx, int enable);
682ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_is_connected(const struct hdmi_cec_device* dev, int port_id);
692ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
702ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic ssize_t read_node(const char *path, char *data)
712ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
722ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = 0;
732ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    FILE *fp = NULL;
742ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    err = access(path, R_OK);
752ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (!err) {
762ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        fp = fopen(path, "r");
772ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if (fp) {
782ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            err = fread(data, sizeof(char), MAX_SYSFS_DATA ,fp);
792ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            fclose(fp);
802ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
812ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
822ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return err;
832ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
842ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
852ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic ssize_t write_node(const char *path, const char *data, size_t len)
862ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
872ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = 0;
882ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int fd = -1;
892ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    err = access(path, W_OK);
902ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (!err) {
912ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        fd = open(path, O_WRONLY);
922ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        errno = 0;
932ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        err = write(fd, data, len);
942ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if (err < 0) {
952ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            err = -errno;
962ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
972ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        close(fd);
982ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    } else {
992ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGE("%s: Failed to access path: %s error: %s",
1002ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                __FUNCTION__, path, strerror(errno));
1012ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        err = -errno;
1022ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
1032ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return err;
1042ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1052ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1062ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel// Helper function to write integer values to the full sysfs path
1072ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic ssize_t write_int_to_node(cec_context_t *ctx,
1082ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        const char *path_postfix,
1092ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        const int value)
1102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char sysfs_full_path[MAX_PATH_LENGTH];
1122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char sysfs_data[MAX_SYSFS_DATA];
1132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    snprintf(sysfs_data, sizeof(sysfs_data), "%d",value);
1142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    snprintf(sysfs_full_path,sizeof(sysfs_full_path), "%s/%s",
1152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->fb_sysfs_path, path_postfix);
1162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = write_node(sysfs_full_path, sysfs_data, strlen(sysfs_data));
1172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return err;
1182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1192ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1202ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void hex_to_string(const char *msg, ssize_t len, char *str)
1212ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1222ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Functions assumes sufficient memory in str
1232ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char *ptr = str;
1242ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    for(int i=0; i < len ; i++) {
1252ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ptr += snprintf(ptr, 3,  "%02X", msg[i]);
1262ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        // Overwrite null termination of snprintf in all except the last byte
1272ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if (i < len - 1)
1282ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            *ptr = ':';
1292ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ptr++;
1302ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
1312ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1322ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1332ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic ssize_t cec_get_fb_node_number(cec_context_t *ctx)
1342ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1352ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //XXX: Do this from a common utility library across the display HALs
1362ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    const int MAX_FB_DEVICES = 2;
1372ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t len = 0;
1382ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char fb_type_path[MAX_PATH_LENGTH];
1392ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char fb_type[MAX_SYSFS_DATA];
1402ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    const char *dtv_panel_str = "dtv panel";
1412ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1422ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    for(int num = 0; num < MAX_FB_DEVICES; num++) {
1432ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        snprintf(fb_type_path, sizeof(fb_type_path),"%s%d/msm_fb_type",
1442ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                SYSFS_BASE,num);
1452ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGD_IF(DEBUG, "%s: num: %d fb_type_path: %s", __FUNCTION__, num, fb_type_path);
1462ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        len = read_node(fb_type_path, fb_type);
1472ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGD_IF(DEBUG, "%s: fb_type:%s", __FUNCTION__, fb_type);
1482ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if(len > 0 && (strncmp(fb_type, dtv_panel_str, strlen(dtv_panel_str)) == 0)){
1492ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGD_IF(DEBUG, "%s: Found DTV panel at fb%d", __FUNCTION__, num);
1502ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->fb_num = num;
1512ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            snprintf(ctx->fb_sysfs_path, sizeof(ctx->fb_sysfs_path),
1522ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                    "%s%d", SYSFS_BASE, num);
1532ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            break;
1542ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
1552ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
1562ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (len < 0)
1572ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return len;
1582ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    else
1592ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return 0;
1602ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1612ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1622ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_add_logical_address(const struct hdmi_cec_device* dev,
1632ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        cec_logical_address_t addr)
1642ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1652ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (addr <  CEC_ADDR_TV || addr > CEC_ADDR_BROADCAST) {
1662ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGE("%s: Received invalid address: %d ", __FUNCTION__, addr);
1672ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return -EINVAL;
1682ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
1692ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
1702ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->logical_address[addr] = LOGICAL_ADDRESS_SET;
1712ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1722ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //XXX: We can get multiple logical addresses here but we can only send one
1732ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //to the driver. Store locally for now
1742ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = write_int_to_node(ctx, "cec/logical_addr", addr);
1752ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGI("%s: Allocated logical address: %d ", __FUNCTION__, addr);
1762ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return (int) err;
1772ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1782ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1792ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_clear_logical_address(const struct hdmi_cec_device* dev)
1802ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1812ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
1822ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    memset(ctx->logical_address, LOGICAL_ADDRESS_UNSET,
1832ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            sizeof(ctx->logical_address));
1842ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //XXX: Find logical_addr that needs to be reset
1852ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    write_int_to_node(ctx, "cec/logical_addr", 15);
1862ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Cleared logical addresses", __FUNCTION__);
1872ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
1882ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
1892ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_get_physical_address(const struct hdmi_cec_device* dev,
1902ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        uint16_t* addr)
1912ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
1922ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
1932ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char pa_path[MAX_PATH_LENGTH];
1942ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char pa_data[MAX_SYSFS_DATA];
1952ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    snprintf (pa_path, sizeof(pa_path),"%s/pa",
1962ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->fb_sysfs_path);
1972ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int err = (int) read_node(pa_path, pa_data);
1982ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    *addr = (uint16_t) atoi(pa_data);
1992ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Physical Address: 0x%x", __FUNCTION__, *addr);
2002ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (err < 0)
2012ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return err;
2022ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    else
2032ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return 0;
2042ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
2052ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2062ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_send_message(const struct hdmi_cec_device* dev,
2072ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        const cec_message_t* msg)
2082ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
2092ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ATRACE_CALL();
2102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(cec_is_connected(dev, 0) <= 0)
2112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return HDMI_RESULT_FAIL;
2122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
2142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: initiator: %d destination: %d length: %u",
2152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            __FUNCTION__, msg->initiator, msg->destination,
2162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            (uint32_t) msg->length);
2172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // Dump message received from framework
2192ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char dump[128];
2202ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(msg->length > 0) {
2212ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        hex_to_string((char*)msg->body, msg->length, dump);
2222ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGD_IF(DEBUG, "%s: message from framework: %s", __FUNCTION__, dump);
2232ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
2242ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2252ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char write_msg_path[MAX_PATH_LENGTH];
2262ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char write_msg[MAX_CEC_FRAME_SIZE];
2272ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    memset(write_msg, 0, sizeof(write_msg));
2282ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // See definition of struct hdmi_cec_msg in driver code
2292ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // drivers/video/msm/mdss/mdss_hdmi_cec.c
2302ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // Write header block
2312ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // XXX: Include this from header in kernel
2322ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    write_msg[CEC_OFFSET_SENDER_ID] = msg->initiator;
2332ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    write_msg[CEC_OFFSET_RECEIVER_ID] = msg->destination;
2342ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Kernel splits opcode/operand, but Android sends it in one byte array
2352ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    write_msg[CEC_OFFSET_OPCODE] = msg->body[0];
2362ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(msg->length > 1) {
2372ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        memcpy(&write_msg[CEC_OFFSET_OPERAND], &msg->body[1],
2382ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                sizeof(char)*(msg->length - 1));
2392ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
2402ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //msg length + initiator + destination
2412ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    write_msg[CEC_OFFSET_FRAME_LENGTH] = (unsigned char) (msg->length + 1);
2422ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    hex_to_string(write_msg, sizeof(write_msg), dump);
2432ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: message to driver: %s", __FUNCTION__, dump);
2442ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    snprintf(write_msg_path, sizeof(write_msg_path), "%s/cec/wr_msg",
2452ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->fb_sysfs_path);
2462ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int retry_count = 0;
2472ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = 0;
2482ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //HAL spec requires us to retry at least once.
2492ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    while (true) {
2502ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        err = write_node(write_msg_path, write_msg, sizeof(write_msg));
2512ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        retry_count++;
2522ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if (err == -EAGAIN && retry_count <= MAX_SEND_MESSAGE_RETRIES) {
2532ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGE("%s: CEC line busy, retrying", __FUNCTION__);
2542ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        } else {
2552ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            break;
2562ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
2572ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
2582ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2592ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (err < 0) {
2602ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel       if (err == -ENXIO) {
2612ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel           ALOGI("%s: No device exists with the destination address",
2622ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                   __FUNCTION__);
2632ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel           return HDMI_RESULT_NACK;
2642ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel       } else if (err == -EAGAIN) {
2652ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGE("%s: CEC line is busy, max retry count exceeded",
2662ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                    __FUNCTION__);
2672ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            return HDMI_RESULT_BUSY;
2682ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        } else {
2692ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            return HDMI_RESULT_FAIL;
2702ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGE("%s: Failed to send CEC message err: %zd - %s",
2712ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                    __FUNCTION__, err, strerror(int(-err)));
2722ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
2732ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    } else {
2742ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGD_IF(DEBUG, "%s: Sent CEC message - %zd bytes written",
2752ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                __FUNCTION__, err);
2762ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return HDMI_RESULT_SUCCESS;
2772ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
2782ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
2792ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2802ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelvoid cec_receive_message(cec_context_t *ctx, char *msg, ssize_t len)
2812ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
2822ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(!ctx->system_control)
2832ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return;
2842ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2852ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char dump[128];
2862ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(len > 0) {
2872ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        hex_to_string(msg, len, dump);
2882ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGD_IF(DEBUG, "%s: Message from driver: %s", __FUNCTION__, dump);
2892ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
2902ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
2912ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    hdmi_event_t event;
2922ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.type = HDMI_EVENT_CEC_MESSAGE;
2932ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.dev = (hdmi_cec_device *) ctx;
2942ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // Remove initiator/destination from this calculation
2952ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.cec.length = msg[CEC_OFFSET_FRAME_LENGTH] - 1;
2962ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.cec.initiator = (cec_logical_address_t) msg[CEC_OFFSET_SENDER_ID];
2972ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.cec.destination = (cec_logical_address_t) msg[CEC_OFFSET_RECEIVER_ID];
2982ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Copy opcode and operand
2992ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    memcpy(event.cec.body, &msg[CEC_OFFSET_OPCODE], event.cec.length);
3002ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    hex_to_string((char *) event.cec.body, event.cec.length, dump);
3012ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Message to framework: %s", __FUNCTION__, dump);
3022ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->callback.callback_func(&event, ctx->callback.callback_arg);
3032ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3042ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3052ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelvoid cec_hdmi_hotplug(cec_context_t *ctx, int connected)
3062ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3072ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Ignore unplug events when system control is disabled
3082ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(!ctx->system_control && connected == 0)
3092ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return;
3102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    hdmi_event_t event;
3112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.type = HDMI_EVENT_HOT_PLUG;
3122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.dev = (hdmi_cec_device *) ctx;
3132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    event.hotplug.connected = connected ? HDMI_CONNECTED : HDMI_NOT_CONNECTED;
3142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->callback.callback_func(&event, ctx->callback.callback_arg);
3152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_register_event_callback(const struct hdmi_cec_device* dev,
3182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            event_callback_t callback, void* arg)
3192ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3202ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Registering callback", __FUNCTION__);
3212ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3222ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->callback.callback_func = callback;
3232ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->callback.callback_arg = arg;
3242ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3252ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3262ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_get_version(const struct hdmi_cec_device* dev, int* version)
3272ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3282ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3292ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    *version = ctx->version;
3302ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: version: %d", __FUNCTION__, *version);
3312ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3322ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3332ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_get_vendor_id(const struct hdmi_cec_device* dev,
3342ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        uint32_t* vendor_id)
3352ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3362ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3372ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    *vendor_id = ctx->vendor_id;
3382ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: vendor id: %u", __FUNCTION__, *vendor_id);
3392ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3402ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3412ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_get_port_info(const struct hdmi_cec_device* dev,
3422ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            struct hdmi_port_info* list[], int* total)
3432ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3442ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Get port info", __FUNCTION__);
3452ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3462ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    *total = NUM_HDMI_PORTS;
3472ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    *list = ctx->port_info;
3482ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3492ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3502ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_set_option(const struct hdmi_cec_device* dev, int flag,
3512ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        int value)
3522ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3532ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3542ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    switch (flag) {
3552ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        case HDMI_OPTION_WAKEUP:
3562ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGD_IF(DEBUG, "%s: Wakeup: value: %d", __FUNCTION__, value);
3572ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            //XXX
3582ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            break;
3592ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        case HDMI_OPTION_ENABLE_CEC:
3602ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGD_IF(DEBUG, "%s: Enable CEC: value: %d", __FUNCTION__, value);
3612ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            cec_enable(ctx, value? 1 : 0);
3622ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            break;
3632ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        case HDMI_OPTION_SYSTEM_CEC_CONTROL:
3642ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ALOGD_IF(DEBUG, "%s: system_control: value: %d",
3652ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                    __FUNCTION__, value);
3662ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->system_control = !!value;
3672ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            break;
3682ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
3692ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3702ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3712ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_set_audio_return_channel(const struct hdmi_cec_device* dev,
3722ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        int port, int flag)
3732ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3742ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3752ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->arc_enabled = flag ? true : false;
3762ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: ARC flag: %d port: %d", __FUNCTION__, flag, port);
3772ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3782ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3792ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_is_connected(const struct hdmi_cec_device* dev, int port_id)
3802ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
3812ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // Ignore port_id since we have only one port
3822ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int connected = 0;
3832ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
3842ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char connected_path[MAX_PATH_LENGTH];
3852ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    char connected_data[MAX_SYSFS_DATA];
3862ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    snprintf (connected_path, sizeof(connected_path),"%s/connected",
3872ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            ctx->fb_sysfs_path);
3882ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err = read_node(connected_path, connected_data);
3892ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    connected = atoi(connected_data);
3902ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3912ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: HDMI at port %d is - %s", __FUNCTION__, port_id,
3922ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            connected ? "connected":"disconnected");
3932ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (err < 0)
3942ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return (int) err;
3952ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    else
3962ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return connected;
3972ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
3982ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
3992ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_device_close(struct hw_device_t *dev)
4002ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
4012ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Close CEC HAL ", __FUNCTION__);
4022ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (!dev) {
4032ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGE("%s: NULL device pointer", __FUNCTION__);
4042ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return -EINVAL;
4052ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
4062ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_context_t* ctx = (cec_context_t*)(dev);
4072ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_close_context(ctx);
4082ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    free(dev);
4092ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return 0;
4102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
4112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_enable(cec_context_t *ctx, int enable)
4132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
4142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ssize_t err;
4152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    // Enable CEC
4162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int value = enable ? 0x3 : 0x0;
4172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    err = write_int_to_node(ctx, "cec/enable", value);
4182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if(err < 0) {
4192ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        ALOGE("%s: Failed to toggle CEC: enable: %d",
4202ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel                __FUNCTION__, enable);
4212ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        return (int) err;
4222ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
4232ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->enabled = enable;
4242ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return 0;
4252ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
4262ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4272ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_init_context(cec_context_t *ctx)
4282ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
4292ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: Initializing context", __FUNCTION__);
4302ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_get_fb_node_number(ctx);
4312ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4322ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Initialize ports - We support only one output port
4332ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->port_info = new hdmi_port_info[NUM_HDMI_PORTS];
4342ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->port_info[0].type = HDMI_OUTPUT;
4352ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->port_info[0].port_id = 1;
4362ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->port_info[0].cec_supported = 1;
4372ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //XXX: Enable ARC if supported
4382ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->port_info[0].arc_supported = 0;
4392ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_get_physical_address((hdmi_cec_device *) ctx,
4402ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            &ctx->port_info[0].physical_address );
4412ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4422ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->version = 0x4;
4432ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->vendor_id = 0xA47733;
4442ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_clear_logical_address((hdmi_cec_device_t*)ctx);
4452ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4462ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Set up listener for HDMI events
4472ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->disp_client = new qClient::QHDMIClient();
4482ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->disp_client->setCECContext(ctx);
4492ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ctx->disp_client->registerClient(ctx->disp_client);
4502ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4512ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    //Enable CEC - framework expects it to be enabled by default
4522ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    cec_enable(ctx, true);
4532ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4542ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD("%s: CEC enabled", __FUNCTION__);
4552ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
4562ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4572ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic void cec_close_context(cec_context_t* ctx __unused)
4582ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
4592ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD("%s: Closing context", __FUNCTION__);
4602ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
4612ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4622ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic int cec_device_open(const struct hw_module_t* module,
4632ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        const char* name,
4642ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        struct hw_device_t** device)
4652ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel{
4662ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    ALOGD_IF(DEBUG, "%s: name: %s", __FUNCTION__, name);
4672ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    int status = -EINVAL;
4682ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    if (!strcmp(name, HDMI_CEC_HARDWARE_INTERFACE )) {
4692ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        struct cec_context_t *dev;
4702ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        dev = (cec_context_t *) calloc (1, sizeof(*dev));
4712ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        if (dev) {
4722ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            cec_init_context(dev);
4732ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4742ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            //Setup CEC methods
4752ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.common.tag       = HARDWARE_DEVICE_TAG;
4762ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.common.version   = HDMI_CEC_DEVICE_API_VERSION_1_0;
4772ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.common.module    = const_cast<hw_module_t* >(module);
4782ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.common.close     = cec_device_close;
4792ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.add_logical_address = cec_add_logical_address;
4802ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.clear_logical_address = cec_clear_logical_address;
4812ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.get_physical_address = cec_get_physical_address;
4822ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.send_message = cec_send_message;
4832ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.register_event_callback = cec_register_event_callback;
4842ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.get_version = cec_get_version;
4852ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.get_vendor_id = cec_get_vendor_id;
4862ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.get_port_info = cec_get_port_info;
4872ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.set_option = cec_set_option;
4882ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.set_audio_return_channel = cec_set_audio_return_channel;
4892ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            dev->device.is_connected = cec_is_connected;
4902ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
4912ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            *device = &dev->device.common;
4922ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            status = 0;
4932ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        } else {
4942ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel            status = -EINVAL;
4952ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        }
4962ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
4972ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    return status;
4982ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}
4992ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel}; //namespace qhdmicec
5002ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
5012ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel// Standard HAL module, should be outside qhdmicec namespace
5022ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelstatic struct hw_module_methods_t cec_module_methods = {
5032ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .open = qhdmicec::cec_device_open
5042ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel};
5052ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
5062ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudelhdmi_module_t HAL_MODULE_INFO_SYM = {
5072ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    .common = {
5082ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .tag = HARDWARE_MODULE_TAG,
5092ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .version_major = 1,
5102ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .version_minor = 0,
5112ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .id = HDMI_CEC_HARDWARE_MODULE_ID,
5122ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .name = "QTI HDMI CEC module",
5132ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .author = "The Linux Foundation",
5142ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel        .methods = &cec_module_methods,
5152ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel    }
5162ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel};
5172ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
5182ebc25b87caae537b8cd97beb8a86a7ff5f0cdf4Thierry Strudel
519