diff --git a/packages/devel/libcec/package.mk b/packages/devel/libcec/package.mk index f81a0b1099..6d849645ac 100644 --- a/packages/devel/libcec/package.mk +++ b/packages/devel/libcec/package.mk @@ -43,9 +43,13 @@ else fi if [ "$KODIPLAYER_DRIVER" = "libamcodec" ]; then - EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DHAVE_AMLOGIC_API=1" + if [ "$PROJECT" = "Odroid_C2" ]; then + EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DHAVE_AOCEC_API=1" + else + EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DHAVE_AMLOGIC_API=1" + fi else - EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DHAVE_AMLOGIC_API=0" + EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DHAVE_AOCEC_API=0 -DHAVE_AMLOGIC_API=0" fi configure_target() { diff --git a/projects/Odroid_C2/linux/linux.aarch64.conf b/projects/Odroid_C2/linux/linux.aarch64.conf index 66a78ba5a7..9708ff64dc 100644 --- a/projects/Odroid_C2/linux/linux.aarch64.conf +++ b/projects/Odroid_C2/linux/linux.aarch64.conf @@ -767,6 +767,7 @@ CONFIG_MAC80211_LEDS=y # # Amlogic Device Drivers # +CONFIG_AML_AO_CEC=y CONFIG_MESON_TIMER=y CONFIG_AM_UART=y CONFIG_SERIAL_MESON_CONSOLE=y @@ -796,7 +797,6 @@ CONFIG_I2C_AML=y CONFIG_AML_HDMI_TX=y CONFIG_AML_HDMI_TX_20=y # CONFIG_AML_HDMI_TX_14 is not set -CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER=y # CONFIG_AML_RTC is not set CONFIG_AML_VRTC=y CONFIG_AM_IRBLASTER=y diff --git a/projects/Odroid_C2/patches/libcec/libcec-00-aocec-support.patch b/projects/Odroid_C2/patches/libcec/libcec-00-aocec-support.patch new file mode 100644 index 0000000000..00f67c5133 --- /dev/null +++ b/projects/Odroid_C2/patches/libcec/libcec-00-aocec-support.patch @@ -0,0 +1,803 @@ +diff --git a/README.md b/README.md +index dfaf4d6..87491ef 100644 +--- a/README.md ++++ b/README.md +@@ -93,6 +93,12 @@ To compile in support for Exynos devices, you have to pass the argument -DHAVE_E + cmake -DHAVE_EXYNOS_API=1 .. + ``` + ++### AOCEC ++To compile in support for AOCEC devices, you have to pass the argument -DHAVE_AOCEC_API=1 to cmake: ++``` ++cmake -DHAVE_AOCEC_API=1 .. ++``` ++ + ### TDA995x + To compile in support for TDA995x devices, you have to pass the argument -DHAVE_TDA995X_API=1 to cmake: + ``` +diff --git a/include/cectypes.h b/include/cectypes.h +index 0fdd48e..881a805 100644 +--- a/include/cectypes.h ++++ b/include/cectypes.h +@@ -309,6 +309,16 @@ namespace CEC { + #define CEC_EXYNOS_VIRTUAL_COM "Exynos" + + /*! ++ * the path to use for the AOCEC HDMI CEC device ++ */ ++#define CEC_AOCEC_PATH "/dev/cec" ++ ++/*! ++ * the name of the virtual COM port to use for the AOCEC' CEC wire ++ */ ++#define CEC_AOCEC_VIRTUAL_COM "AOCEC" ++ ++/*! + * Mimimum client version + */ + #define CEC_MIN_LIB_VERSION 3 +@@ -877,7 +887,8 @@ typedef enum cec_adapter_type + ADAPTERTYPE_P8_DAUGHTERBOARD = 0x2, + ADAPTERTYPE_RPI = 0x100, + ADAPTERTYPE_TDA995x = 0x200, +- ADAPTERTYPE_EXYNOS = 0x300 ++ ADAPTERTYPE_EXYNOS = 0x300, ++ ADAPTERTYPE_AOCEC = 0x500 + } cec_adapter_type; + + /** force exporting through swig */ +diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt +index a494533..d3eefa3 100644 +--- a/src/libcec/CMakeLists.txt ++++ b/src/libcec/CMakeLists.txt +@@ -87,6 +87,9 @@ set(CEC_HEADERS devices/CECRecordingDevice.h + adapter/Exynos/ExynosCEC.h + adapter/Exynos/ExynosCECAdapterDetection.h + adapter/Exynos/ExynosCECAdapterCommunication.h ++ adapter/AOCEC/AOCEC.h ++ adapter/AOCEC/AOCECAdapterDetection.h ++ adapter/AOCEC/AOCECAdapterCommunication.h + adapter/Pulse-Eight/USBCECAdapterMessageQueue.h + adapter/Pulse-Eight/USBCECAdapterCommunication.h + adapter/Pulse-Eight/USBCECAdapterCommands.h +diff --git a/src/libcec/adapter/AOCEC/AOCEC.h b/src/libcec/adapter/AOCEC/AOCEC.h +new file mode 100644 +index 0000000..560fbdd +--- /dev/null ++++ b/src/libcec/adapter/AOCEC/AOCEC.h +@@ -0,0 +1,55 @@ ++#pragma once ++/* ++ * This file is part of the libCEC(R) library. ++ * ++ * libCEC AOCEC Code Copyright (C) 2016 Gerald Dachs ++ * based heavily on: ++ * libCEC Exynos Code Copyright (C) 2014 Valentin Manea ++ * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. ++ * libCEC(R) is an original work, containing original code. ++ * ++ * libCEC(R) is a trademark of Pulse-Eight Limited. ++ * ++ * This program is dual-licensed; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * ++ * Alternatively, you can license this library under a commercial license, ++ * please contact Pulse-Eight Licensing for more information. ++ * ++ * For more information contact: ++ * Pulse-Eight Licensing ++ * http://www.pulse-eight.com/ ++ * http://www.pulse-eight.net/ ++ */ ++ ++ ++#define CEC_DEFAULT_PADDR 0x1000 ++ ++#define CEC_IOC_MAGIC 'C' ++#define CEC_IOC_GET_PHYSICAL_ADDR _IOR(CEC_IOC_MAGIC, 0x00, uint16_t) ++#define CEC_IOC_GET_VERSION _IOR(CEC_IOC_MAGIC, 0x01, int) ++#define CEC_IOC_GET_VENDOR_ID _IOR(CEC_IOC_MAGIC, 0x02, uint32_t) ++#define CEC_IOC_GET_PORT_INFO _IOR(CEC_IOC_MAGIC, 0x03, int) ++#define CEC_IOC_GET_PORT_NUM _IOR(CEC_IOC_MAGIC, 0x04, int) ++#define CEC_IOC_GET_SEND_FAIL_REASON _IOR(CEC_IOC_MAGIC, 0x05, uint32_t) ++#define CEC_IOC_SET_OPTION_WAKEUP _IOW(CEC_IOC_MAGIC, 0x06, uint32_t) ++#define CEC_IOC_SET_OPTION_ENALBE_CEC _IOW(CEC_IOC_MAGIC, 0x07, uint32_t) ++#define CEC_IOC_SET_OPTION_SYS_CTRL _IOW(CEC_IOC_MAGIC, 0x08, uint32_t) ++#define CEC_IOC_SET_OPTION_SET_LANG _IOW(CEC_IOC_MAGIC, 0x09, uint32_t) ++#define CEC_IOC_GET_CONNECT_STATUS _IOR(CEC_IOC_MAGIC, 0x0A, uint32_t) ++#define CEC_IOC_ADD_LOGICAL_ADDR _IOW(CEC_IOC_MAGIC, 0x0B, uint32_t) ++#define CEC_IOC_CLR_LOGICAL_ADDR _IOW(CEC_IOC_MAGIC, 0x0C, uint32_t) ++ ++#define CEC_MAX_FRAME_SIZE 16 +diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp +new file mode 100644 +index 0000000..8edf981 +--- /dev/null ++++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp +@@ -0,0 +1,343 @@ ++/* ++ * This file is part of the libCEC(R) library. ++ * ++ * libCEC AOCEC Code Copyright (C) 2016 Gerald Dachs ++ * based heavily on: ++ * libCEC Exynos Code Copyright (C) 2014 Valentin Manea ++ * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. ++ * libCEC(R) is an original work, containing original code. ++ * ++ * libCEC(R) is a trademark of Pulse-Eight Limited. ++ * ++ * This program is dual-licensed; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * ++ * Alternatively, you can license this library under a commercial license, ++ * please contact Pulse-Eight Licensing for more information. ++ * ++ * For more information contact: ++ * Pulse-Eight Licensing ++ * http://www.pulse-eight.com/ ++ * http://www.pulse-eight.net/ ++ */ ++ ++#include "env.h" ++#include ++#include ++ ++ ++#if defined(HAVE_AOCEC_API) ++#include "AOCEC.h" ++#include "AOCECAdapterCommunication.h" ++ ++#include "CECTypeUtils.h" ++#include "LibCEC.h" ++#include ++ ++using namespace CEC; ++using namespace P8PLATFORM; ++ ++#define LIB_CEC m_callback->GetLib() ++ ++ ++CAOCECAdapterCommunication::CAOCECAdapterCommunication(IAdapterCommunicationCallback *callback) : ++ IAdapterCommunication(callback), ++ m_bLogicalAddressChanged(false) ++{ ++ CLockObject lock(m_mutex); ++ ++ m_logicalAddresses.Clear(); ++ m_fd = INVALID_SOCKET_VALUE; ++} ++ ++ ++CAOCECAdapterCommunication::~CAOCECAdapterCommunication(void) ++{ ++ Close(); ++} ++ ++ ++bool CAOCECAdapterCommunication::IsOpen(void) ++{ ++ CLockObject lock(m_mutex); ++ return IsInitialised() && m_fd != INVALID_SOCKET_VALUE; ++} ++ ++ ++bool CAOCECAdapterCommunication::Open(uint32_t UNUSED(iTimeoutMs), bool UNUSED(bSkipChecks), bool bStartListening) ++{ ++ if (IsOpen()) ++ Close(); ++ ++ CLockObject lock(m_mutex); ++ ++ if ((m_fd = open(CEC_AOCEC_PATH, O_RDWR)) > 0) ++ { ++ uint32_t enable = true; ++ ++ if (ioctl(m_fd, CEC_IOC_SET_OPTION_SYS_CTRL, enable)) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL IOCTL CEC_IOC_SET_OPTION_SYS_CTRL failed !", __func__); ++ return false; ++ } ++ ++ if (!bStartListening || CreateThread()) { ++ return true; ++ } ++ close(m_fd); ++ m_fd = INVALID_SOCKET_VALUE; ++ } ++ return false; ++} ++ ++ ++void CAOCECAdapterCommunication::Close(void) ++{ ++ StopThread(0); ++ ++ CLockObject lock(m_mutex); ++ ++ uint32_t enable = false; ++ ++ if (ioctl(m_fd, CEC_IOC_SET_OPTION_SYS_CTRL, enable)) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_SET_OPTION_SYS_CTRL failed !", __func__); ++ } ++ ++ close(m_fd); ++ m_fd = INVALID_SOCKET_VALUE; ++} ++ ++ ++std::string CAOCECAdapterCommunication::GetError(void) const ++{ ++ std::string strError(m_strError); ++ return strError; ++} ++ ++int CAOCECAdapterCommunication::getFileDescriptor(void) ++{ ++ CLockObject lock(m_mutex); ++ ++ return m_fd; ++} ++ ++ ++ ++cec_adapter_message_state CAOCECAdapterCommunication::Write( ++ const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool UNUSED(bIsReply)) ++{ ++ uint8_t buffer[CEC_MAX_FRAME_SIZE]; ++ int32_t size = 1; ++ cec_adapter_message_state rc = ADAPTER_MESSAGE_STATE_ERROR; ++ ++ if (!IsOpen()) ++ return rc; ++ ++ CLockObject lock(m_mutex); ++ ++ if ((size_t)data.parameters.size + data.opcode_set > sizeof(buffer)) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: data size too large !", __func__); ++ return ADAPTER_MESSAGE_STATE_ERROR; ++ } ++ ++ buffer[0] = (data.initiator << 4) | (data.destination & 0x0f); ++ ++ if (data.opcode_set) ++ { ++ buffer[1] = data.opcode; ++ size++; ++ ++ memcpy(&buffer[size], data.parameters.data, data.parameters.size); ++ size += data.parameters.size; ++ } ++ ++ if (write(m_fd, (void *)buffer, size) == size) ++ { ++ rc = ADAPTER_MESSAGE_STATE_SENT_ACKED; ++ } ++ else ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: write failed !", __func__); ++ } ++ ++ return rc; ++} ++ ++ ++uint16_t CAOCECAdapterCommunication::GetFirmwareVersion(void) ++{ ++ int version = 0; ++ ++ if (!IsOpen()) ++ return version; ++ ++ CLockObject lock(m_mutex); ++ ++ if (ioctl(m_fd, CEC_IOC_GET_VERSION, &version) < 0) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_GET_VERSION failed !", __func__); ++ } ++ return (uint16_t)version; ++} ++ ++ ++cec_vendor_id CAOCECAdapterCommunication::GetVendorId(void) ++{ ++ int vendor_id = CEC_VENDOR_UNKNOWN; ++ ++ if (!IsOpen()) ++ return cec_vendor_id(vendor_id); ++ ++ CLockObject lock(m_mutex); ++ ++ if (ioctl(m_fd, CEC_IOC_GET_VENDOR_ID, &vendor_id) < 0) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_GET_VENDOR_ID failed !", __func__); ++ } ++ return cec_vendor_id(vendor_id); ++} ++ ++ ++uint16_t CAOCECAdapterCommunication::GetPhysicalAddress(void) ++{ ++ int phys_addr = CEC_DEFAULT_PADDR; ++ ++ if (!IsOpen()) ++ return (uint16_t)phys_addr; ++ ++ CLockObject lock(m_mutex); ++ ++ if (ioctl(m_fd, CEC_IOC_GET_PHYSICAL_ADDR, &phys_addr) < 0) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_GET_PHYSICAL_ADDR failed !", __func__); ++ phys_addr = CEC_DEFAULT_PADDR; ++ } ++ return (uint16_t)phys_addr; ++} ++ ++ ++cec_logical_addresses CAOCECAdapterCommunication::GetLogicalAddresses(void) ++{ ++ return m_logicalAddresses; ++} ++ ++ ++bool CAOCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) ++{ ++ unsigned int log_addr = addresses.primary; ++ if (!IsOpen()) ++ return false; ++ ++ CLockObject lock(m_mutex); ++ ++ if (ioctl(m_fd, CEC_IOC_ADD_LOGICAL_ADDR, log_addr)) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_ADD_LOGICAL_ADDR failed !", __func__); ++ return false; ++ } ++ m_logicalAddresses = addresses; ++ m_bLogicalAddressChanged = true; ++ ++ return true; ++} ++ ++ ++void CAOCECAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)) ++{ ++ unsigned int log_addr = CECDEVICE_BROADCAST; ++ ++ if (!IsOpen()) ++ return; ++ ++ CLockObject lock(m_mutex); ++ ++ if (ioctl(m_fd, CEC_IOC_ADD_LOGICAL_ADDR, log_addr)) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: IOCTL CEC_IOC_ADD_LOGICAL_ADDR failed !", __func__); ++ } ++} ++ ++ ++void *CAOCECAdapterCommunication::Process(void) ++{ ++ uint8_t buffer[CEC_MAX_FRAME_SIZE]; ++ uint32_t size; ++ fd_set rfds; ++ cec_logical_address initiator, destination; ++ struct timeval tv; ++ ++ if (!IsOpen()) ++ return 0; ++ ++ while (!IsStopped()) ++ { ++ int fd = getFileDescriptor(); ++ ++ if (fd == INVALID_SOCKET_VALUE) ++ { ++ break; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_SET(fd, &rfds); ++ ++ tv.tv_sec = 1; ++ tv.tv_usec = 0; ++ ++ if (select(fd + 1, &rfds, NULL, NULL, &tv) >= 0 ) ++ { ++ ++ if (!FD_ISSET(fd, &rfds)) ++ continue; ++ ++ size = read(fd, buffer, CEC_MAX_FRAME_SIZE); ++ ++ if (size > 0) ++ { ++#if 0 // currently unused ++ if (buffer[0] == 0xff) // driver wants us to reread the physical address ++ { ++ if (!IsStopped()) ++ { ++ uint16_t iNewAddress = GetPhysicalAddress(); ++ m_callback->HandlePhysicalAddressChanged(iNewAddress); ++ } ++ continue; ++ } ++#endif ++ initiator = cec_logical_address(buffer[0] >> 4); ++ destination = cec_logical_address(buffer[0] & 0x0f); ++ ++ cec_command cmd; ++ ++ cec_command::Format( ++ cmd, initiator, destination, ++ ( size > 1 ) ? cec_opcode(buffer[1]) : CEC_OPCODE_NONE); ++ ++ for( uint8_t i = 2; i < size; i++ ) ++ cmd.parameters.PushBack(buffer[i]); ++ ++ if (!IsStopped()) ++ m_callback->OnCommandReceived(cmd); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++#endif // HAVE_AOCEC_API +diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +new file mode 100644 +index 0000000..f6a8a4a +--- /dev/null ++++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +@@ -0,0 +1,105 @@ ++#pragma once ++/* ++ * This file is part of the libCEC(R) library. ++ * ++ * libCEC AOCEC Code Copyright (C) 2016 Gerald Dachs ++ * based heavily on: ++ * libCEC Exynos Code Copyright (C) 2014 Valentin Manea ++ * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. ++ * libCEC(R) is an original work, containing original code. ++ * ++ * libCEC(R) is a trademark of Pulse-Eight Limited. ++ * ++ * This program is dual-licensed; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * ++ * Alternatively, you can license this library under a commercial license, ++ * please contact Pulse-Eight Licensing for more information. ++ * ++ * For more information contact: ++ * Pulse-Eight Licensing ++ * http://www.pulse-eight.com/ ++ * http://www.pulse-eight.net/ ++ */ ++ ++#include "env.h" ++ ++#if defined(HAVE_AOCEC_API) ++ ++#include ++#include ++#include "../AdapterCommunication.h" ++#include ++ ++namespace CEC ++{ ++ class CAOCECAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread ++ { ++ public: ++ /*! ++ * @brief Create a new Exynos HDMI CEC communication handler. ++ * @param callback The callback to use for incoming CEC commands. ++ */ ++ CAOCECAdapterCommunication(IAdapterCommunicationCallback *callback); ++ virtual ~CAOCECAdapterCommunication(void); ++ ++ /** @name IAdapterCommunication implementation */ ++ ///{ ++ bool Open(uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT, bool bSkipChecks = false, bool bStartListening = true); ++ void Close(void); ++ bool IsOpen(void); ++ std::string GetError(void) const; ++ cec_adapter_message_state Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply); ++ ++ bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } ++ bool StartBootloader(void) { return false; } ++ bool SetLogicalAddresses(const cec_logical_addresses &addresses); ++ cec_logical_addresses GetLogicalAddresses(void); ++ bool PingAdapter(void) { return IsInitialised(); } ++ uint16_t GetFirmwareVersion(void); ++ uint32_t GetFirmwareBuildDate(void) { return 0; } ++ bool IsRunningLatestFirmware(void) { return true; } ++ bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } ++ bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } ++ std::string GetPortName(void) { return std::string("AOCEC"); } ++ uint16_t GetPhysicalAddress(void); ++ bool SetControlledMode(bool UNUSED(controlled)) { return true; } ++ cec_vendor_id GetVendorId(void); ++ bool SupportsSourceLogicalAddress(const cec_logical_address address) { return address > CECDEVICE_TV && address <= CECDEVICE_BROADCAST; } ++ cec_adapter_type GetAdapterType(void) { return ADAPTERTYPE_AOCEC; } ++ uint16_t GetAdapterVendorId(void) const { return 1; } ++ uint16_t GetAdapterProductId(void) const { return 1; } ++ void HandleLogicalAddressLost(cec_logical_address oldAddress); ++ void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} ++ ///} ++ ++ /** @name P8PLATFORM::CThread implementation */ ++ ///{ ++ void *Process(void); ++ ///} ++ ++ private: ++ bool IsInitialised(void) const { return 1; }; ++ int getFileDescriptor(void); ++ ++ std::string m_strError; /**< current error message */ ++ ++ bool m_bLogicalAddressChanged; ++ cec_logical_addresses m_logicalAddresses; ++ P8PLATFORM::CMutex m_mutex; ++ int m_fd; ++ }; ++}; ++#endif +diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterDetection.cpp b/src/libcec/adapter/AOCEC/AOCECAdapterDetection.cpp +new file mode 100644 +index 0000000..55f3ee0 +--- /dev/null ++++ b/src/libcec/adapter/AOCEC/AOCECAdapterDetection.cpp +@@ -0,0 +1,50 @@ ++/* ++ * This file is part of the libCEC(R) library. ++ * ++ * libCEC AOCEC Code Copyright (C) 2016 Gerald Dachs ++ * based heavily on: ++ * libCEC Exynos Code Copyright (C) 2014 Valentin Manea ++ * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. ++ * libCEC(R) is an original work, containing original code. ++ * ++ * libCEC(R) is a trademark of Pulse-Eight Limited. ++ * ++ * This program is dual-licensed; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * ++ * Alternatively, you can license this library under a commercial license, ++ * please contact Pulse-Eight Licensing for more information. ++ * ++ * For more information contact: ++ * Pulse-Eight Licensing ++ * http://www.pulse-eight.com/ ++ * http://www.pulse-eight.net/ ++ */ ++ ++#include "env.h" ++#include ++ ++#if defined(HAVE_AOCEC_API) ++#include "AOCECAdapterDetection.h" ++#include "AOCEC.h" ++ ++using namespace CEC; ++ ++bool CAOCECAdapterDetection::FindAdapter(void) ++{ ++ return access(CEC_AOCEC_PATH, 0) == 0; ++} ++ ++#endif +diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterDetection.h b/src/libcec/adapter/AOCEC/AOCECAdapterDetection.h +new file mode 100644 +index 0000000..cacaa19 +--- /dev/null ++++ b/src/libcec/adapter/AOCEC/AOCECAdapterDetection.h +@@ -0,0 +1,46 @@ ++#pragma once ++/* ++ * This file is part of the libCEC(R) library. ++ * ++ * libCEC AOCEC Code Copyright (C) 2016 Gerald Dachs ++ * based heavily on: ++ * libCEC Exynos Code Copyright (C) 2014 Valentin Manea ++ * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. ++ * libCEC(R) is an original work, containing original code. ++ * ++ * libCEC(R) is a trademark of Pulse-Eight Limited. ++ * ++ * This program is dual-licensed; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * ++ * Alternatively, you can license this library under a commercial license, ++ * please contact Pulse-Eight Licensing for more information. ++ * ++ * For more information contact: ++ * Pulse-Eight Licensing ++ * http://www.pulse-eight.com/ ++ * http://www.pulse-eight.net/ ++ */ ++ ++#include "env.h" ++ ++namespace CEC ++{ ++ class CAOCECAdapterDetection ++ { ++ public: ++ static bool FindAdapter(void); ++ }; ++} +diff --git a/src/libcec/adapter/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp +index da05725..1e946e6 100644 +--- a/src/libcec/adapter/AdapterFactory.cpp ++++ b/src/libcec/adapter/AdapterFactory.cpp +@@ -58,6 +58,11 @@ + #include "Exynos/ExynosCECAdapterCommunication.h" + #endif + ++#if defined(HAVE_AOCEC_API) ++#include "AOCEC/AOCECAdapterDetection.h" ++#include "AOCEC/AOCECAdapterCommunication.h" ++#endif ++ + using namespace CEC; + + int8_t CAdapterFactory::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +@@ -126,8 +131,20 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 + } + #endif + ++#if defined(HAVE_AOCEC_API) ++ if (iAdaptersFound < iBufSize && CAOCECAdapterDetection::FindAdapter()) ++ { ++ snprintf(deviceList[iAdaptersFound].strComPath, sizeof(deviceList[iAdaptersFound].strComPath), CEC_AOCEC_PATH); ++ snprintf(deviceList[iAdaptersFound].strComName, sizeof(deviceList[iAdaptersFound].strComName), CEC_AOCEC_VIRTUAL_COM); ++ deviceList[iAdaptersFound].iVendorId = 0; ++ deviceList[iAdaptersFound].iProductId = 0; ++ deviceList[iAdaptersFound].adapterType = ADAPTERTYPE_AOCEC; ++ iAdaptersFound++; ++ } ++#endif ++ + +-#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) ++#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_AOCEC_API) + #error "libCEC doesn't have support for any type of adapter. please check your build system or configuration" + #endif + +@@ -146,6 +163,11 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ + return new CExynosCECAdapterCommunication(m_lib->m_cec); + #endif + ++#if defined(HAVE_AOCEC_API) ++ if (!strcmp(strPort, CEC_AOCEC_VIRTUAL_COM)) ++ return new CAOCECAdapterCommunication(m_lib->m_cec); ++#endif ++ + #if defined(HAVE_RPI_API) + if (!strcmp(strPort, CEC_RPI_VIRTUAL_COM)) + return new CRPiCECAdapterCommunication(m_lib->m_cec); +diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake +index b8a16c8..93a97e6 100644 +--- a/src/libcec/cmake/CheckPlatformSupport.cmake ++++ b/src/libcec/cmake/CheckPlatformSupport.cmake +@@ -9,6 +9,7 @@ + # HAVE_RPI_API 1 if Raspberry Pi is supported + # HAVE_TDA995X_API 1 if TDA995X is supported + # HAVE_EXYNOS_API 1 if Exynos is supported ++# HAVE_AOCEC_API 1 if AOCEC is supported + # HAVE_P8_USB 1 if Pulse-Eight devices are supported + # HAVE_P8_USB_DETECT 1 if Pulse-Eight devices can be auto-detected + # HAVE_DRM_EDID_PARSER 1 if DRM EDID parsing is supported +@@ -132,6 +133,18 @@ else() + else() + set(HAVE_EXYNOS_API 0) + endif() ++ ++ # AOCEC ++ if (${HAVE_AOCEC_API}) ++ set(LIB_INFO "${LIB_INFO}, AOCEC") ++ set(HAVE_AOCEC_API 1) ++ set(CEC_SOURCES_ADAPTER_AOCEC adapter/AOCEC/AOCECAdapterDetection.cpp ++ adapter/AOCEC/AOCECAdapterCommunication.cpp) ++ source_group("Source Files\\adapter\\AOCEC" FILES ${CEC_SOURCES_ADAPTER_AOCEC}) ++ list(APPEND CEC_SOURCES ${CEC_SOURCES_ADAPTER_AOCEC}) ++ else() ++ set(HAVE_AOCEC_API 0) ++ endif() + endif() + + # rt +diff --git a/src/libcec/cmake/DisplayPlatformSupport.cmake b/src/libcec/cmake/DisplayPlatformSupport.cmake +index 9dcaacf..7ec10f5 100644 +--- a/src/libcec/cmake/DisplayPlatformSupport.cmake ++++ b/src/libcec/cmake/DisplayPlatformSupport.cmake +@@ -44,6 +44,12 @@ else() + message(STATUS "DRM support: no") + endif() + ++if (HAVE_AOCEC_API) ++ message(STATUS "AOCEC support: yes") ++else() ++ message(STATUS "AOCEC support: no") ++endif() ++ + if (HAVE_PYTHON) + message(STATUS "Python support: version ${PYTHONLIBS_VERSION_STRING} (${PYTHON_VERSION})") + else() +diff --git a/src/libcec/env.h.in b/src/libcec/env.h.in +index 0496aa0..fe6c83d 100644 +--- a/src/libcec/env.h.in ++++ b/src/libcec/env.h.in +@@ -72,6 +72,9 @@ + /* Define to 1 for Exynos support */ + #cmakedefine HAVE_EXYNOS_API @HAVE_EXYNOS_API@ + ++/* Define to 1 for AOCEC support */ ++#cmakedefine HAVE_AOCEC_API @HAVE_AOCEC_API@ ++ + /* Define to 1 for nVidia EDID parsing support (on selected models) */ + #cmakedefine HAVE_NVIDIA_EDID_PARSER @HAVE_NVIDIA_EDID_PARSER@ + diff --git a/projects/Odroid_C2/patches/linux/linux-999.20-New-amlogic-cec-hybrid-driver.patch b/projects/Odroid_C2/patches/linux/linux-999.20-New-amlogic-cec-hybrid-driver.patch new file mode 100644 index 0000000000..84658b7cc4 --- /dev/null +++ b/projects/Odroid_C2/patches/linux/linux-999.20-New-amlogic-cec-hybrid-driver.patch @@ -0,0 +1,2758 @@ +From f620a493a8506e79fa5adb7b867b6ba4bf95dd2c Mon Sep 17 00:00:00 2001 +From: Radostan Riedel +Date: Thu, 5 May 2016 18:00:34 +0200 +Subject: [PATCH 1/1] New amlogic cec hybrid driver + +--- + arch/arm64/boot/dts/meson64_odroidc2.dts | 19 + + drivers/amlogic/Kconfig | 2 + + drivers/amlogic/Makefile | 2 + + drivers/amlogic/cec/Kconfig | 9 + + drivers/amlogic/cec/Makefile | 1 + + drivers/amlogic/cec/hdmi_ao_cec.c | 2009 ++++++++++++++++++++++++ + drivers/amlogic/cec/hdmi_ao_cec.h | 179 +++ + drivers/amlogic/hdmi/Kconfig | 2 + + drivers/amlogic/hdmi/hdmi_tx_20/Makefile | 3 + + drivers/amlogic/hdmi/hdmi_tx_20/hdmi_tx_main.c | 3 +- + drivers/amlogic/hdmi/hdmi_tx_20/hw/Makefile | 5 +- + drivers/amlogic/hdmi/hdmi_tx_20/hw/reg_ops.c | 4 + + include/linux/amlogic/hdmi_tx/hdmi_tx_cec_20.h | 356 +++++ + 13 files changed, 2592 insertions(+), 2 deletions(-) + create mode 100644 drivers/amlogic/cec/Kconfig + create mode 100644 drivers/amlogic/cec/Makefile + create mode 100644 drivers/amlogic/cec/hdmi_ao_cec.c + create mode 100644 drivers/amlogic/cec/hdmi_ao_cec.h + create mode 100644 include/linux/amlogic/hdmi_tx/hdmi_tx_cec_20.h + +diff --git a/arch/arm64/boot/dts/meson64_odroidc2.dts b/arch/arm64/boot/dts/meson64_odroidc2.dts +index b47490b..7d9b4cf 100644 +--- a/arch/arm64/boot/dts/meson64_odroidc2.dts ++++ b/arch/arm64/boot/dts/meson64_odroidc2.dts +@@ -410,6 +410,25 @@ + }; + }; + ++ aocec: aocec{ ++ compatible = "amlogic, amlogic-aocec"; ++ device_name = "aocec"; ++ status = "okay"; ++ vendor_name = "Hardkernel"; /* Max Chars: 8 */ ++ vendor_id = <0x000000>; /* Refer to http://standards.ieee.org/develop/regauth/oui/oui.txt */ ++ product_desc = "ODROID-C2"; /* Max Chars: 16 */ ++ cec_osd_string = "ODROID-C2"; /* Max Chars: 14 */ ++ port_num = <3>; ++ arc_port_mask = <0x0>; ++ interrupts = <0 199 1>; ++ interrupt-names = "hdmi_aocec"; ++ pinctrl-names = "hdmitx_aocec"; ++ pinctrl-0=<&hdmitx_aocec>; ++ reg = <0x0 0xc810023c 0x0 0x4 ++ 0x0 0xc8100000 0x0 0x200 ++ 0x0 0xda83e000 0x0 0x10>; ++ }; ++ + uart_AO: serial@c81004c0 { + compatible = "amlogic, meson-uart"; + reg = <0x0 0xc81004c0 0x0 0x14>; +diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig +index 5a2b9d4..c9543f0 100644 +--- a/drivers/amlogic/Kconfig ++++ b/drivers/amlogic/Kconfig +@@ -3,6 +3,8 @@ + # + menu "Amlogic Device Drivers" + ++source "drivers/amlogic/cec/Kconfig" ++ + source "drivers/amlogic/clocksource/Kconfig" + + source "drivers/amlogic/uart/Kconfig" +diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile +index 5f1ce80..92ce12b 100644 +--- a/drivers/amlogic/Makefile ++++ b/drivers/amlogic/Makefile +@@ -117,3 +117,5 @@ obj-$(CONFIG_AML_WDT) += watchdog/ + obj-$(CONFIG_INSTABOOT) += instaboot/ + + obj-$(CONFIG_MESON_PWM) += pwm/ ++ ++obj-$(CONFIG_AML_AO_CEC) += cec/ +diff --git a/drivers/amlogic/cec/Kconfig b/drivers/amlogic/cec/Kconfig +new file mode 100644 +index 0000000..ff4d159 +--- /dev/null ++++ b/drivers/amlogic/cec/Kconfig +@@ -0,0 +1,9 @@ ++menu "AO CEC Support" ++ ++config AML_AO_CEC ++ boolean "HDMI AO cec driver support" ++ help ++ HDMI AO cec driver provide cec support on Amlogic SOC chips, you can ++ use this driver to implement cec features on TV/MBOX ++ ++endmenu +diff --git a/drivers/amlogic/cec/Makefile b/drivers/amlogic/cec/Makefile +new file mode 100644 +index 0000000..1c16a4b +--- /dev/null ++++ b/drivers/amlogic/cec/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_AML_AO_CEC) += hdmi_ao_cec.o +diff --git a/drivers/amlogic/cec/hdmi_ao_cec.c b/drivers/amlogic/cec/hdmi_ao_cec.c +new file mode 100644 +index 0000000..6c9b2fd +--- /dev/null ++++ b/drivers/amlogic/cec/hdmi_ao_cec.c +@@ -0,0 +1,2009 @@ ++/* ++ * drivers/amlogic/cec/hdmi_ao_cec.c ++ * ++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++//#include ++#include ++#include ++#include ++#include ++#include "hdmi_ao_cec.h" ++#include ++#include ++#include ++#include ++#ifdef CONFIG_HAS_EARLYSUSPEND ++#include ++static struct early_suspend aocec_suspend_handler; ++#endif ++ ++ ++#define CEC_FRAME_DELAY msecs_to_jiffies(400) ++#define CEC_DEV_NAME "cec" ++ ++#define DEV_TYPE_TX 4 ++#define DEV_TYPE_RX 0 ++ ++#define CEC_EARLY_SUSPEND (1 << 0) ++#define CEC_DEEP_SUSPEND (1 << 1) ++ ++/* global struct for tx and rx */ ++struct ao_cec_dev { ++ unsigned long dev_type; ++ unsigned int port_num; ++ unsigned int arc_port; ++ unsigned int hal_flag; ++ unsigned int phy_addr; ++ unsigned int port_seq; ++ unsigned long irq_cec; ++ void __iomem *exit_reg; ++ void __iomem *cec_reg; ++ void __iomem *hdmi_rxreg; ++ struct hdmitx_dev *tx_dev; ++ struct workqueue_struct *cec_thread; ++ struct device *dbg_dev; ++ struct delayed_work cec_work; ++ struct completion rx_ok; ++ struct completion tx_ok; ++ spinlock_t cec_reg_lock; ++ struct mutex cec_mutex; ++#ifdef CONFIG_PM ++ int cec_suspend; ++#endif ++ struct vendor_info_data v_data; ++ struct cec_global_info_t cec_info; ++}; ++ ++static int phy_addr_test; ++ ++/* from android cec hal */ ++enum { ++ HDMI_OPTION_WAKEUP = 1, ++ HDMI_OPTION_ENABLE_CEC = 2, ++ HDMI_OPTION_SYSTEM_CEC_CONTROL = 3, ++ HDMI_OPTION_SET_LANG = 5, ++}; ++ ++static struct ao_cec_dev *cec_dev; ++static int cec_tx_result; ++ ++static unsigned char rx_msg[MAX_MSG]; ++static unsigned char rx_len; ++static unsigned int new_msg; ++bool cec_msg_dbg_en = 1; ++ ++#define CEC_ERR(format, args...) \ ++ {if (cec_dev->dbg_dev) \ ++ dev_err(cec_dev->dbg_dev, format, ##args); \ ++ } ++ ++#define CEC_INFO(format, args...) \ ++ {if (cec_msg_dbg_en && cec_dev->dbg_dev) \ ++ dev_info(cec_dev->dbg_dev, format, ##args); \ ++ } ++ ++static unsigned char msg_log_buf[128] = { 0 }; ++ ++#define waiting_aocec_free() \ ++ do {\ ++ unsigned long cnt = 0;\ ++ while (readl(cec_dev->cec_reg + AO_CEC_RW_REG) & (1<<23)) {\ ++ if (3500 == cnt++) { \ ++ pr_info("waiting aocec free time out.\n");\ ++ break;\ ++ } \ ++ } \ ++ } while (0) ++ ++#define HR_DELAY(n) (ktime_set(0, n * 1000 * 1000)) ++__u16 cec_key_map[160] = { ++ KEY_ENTER, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 0 , 0 , 0 ,//0x00 ++ 0 , KEY_HOMEPAGE , KEY_MENU, 0, 0, KEY_BACK, 0, 0, ++ 0 , 0, 0, 0, 0, 0, 0, 0,//0x10 ++ 0 , 0, 0, 0, 0, 0, 0, 0, ++ KEY_0 , KEY_1, KEY_2, KEY_3,KEY_4, KEY_5, KEY_6, KEY_7,//0x20 ++ KEY_8 , KEY_9, KEY_DOT, 0, 0, 0, 0, 0, ++ KEY_CHANNELUP , KEY_CHANNELDOWN, KEY_CHANNEL, 0, 0, 0, 0, 0,//0x30 ++ 0 , 0, 0, 0, 0, 0, 0, 0, ++ ++ KEY_POWER , KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, KEY_PLAYPAUSE, KEY_STOP, KEY_PLAYPAUSE, KEY_RECORD,//0x40 ++ KEY_REWIND, KEY_FASTFORWARD, KEY_EJECTCD, KEY_NEXTSONG, KEY_PREVIOUSSONG, 0, 0, 0, ++ 0 , 0, 0, KEY_PROGRAM, 0, 0, 0, 0,//0x50 ++ 0 , 0, 0, 0, 0, 0, 0, 0, ++ KEY_PLAYCD, KEY_PLAYPAUSE, KEY_RECORD, KEY_PAUSECD, KEY_STOPCD, KEY_MUTE, 0, KEY_TUNER,//0x60 ++ 0 , KEY_MEDIA, 0, 0, KEY_POWER, 0, 0, 0, ++ 0 , KEY_BLUE, KEY_RED, KEY_GREEN, KEY_YELLOW, 0, 0, 0,//0x70 ++ 0 , 0, 0, 0, 0, 0, 0, 0x2fd, ++ 0 , 0, 0, 0, 0, 0, 0, 0,//0x80 ++ 0 , 0, 0, 0, 0, 0, 0, 0, ++ 0 , KEY_EXIT, 0, 0, 0, 0, KEY_PVR, 0,//0x90 //samsung vendor buttons return and channel_list ++ 0 , 0, 0, 0, 0, 0, 0, 0, ++}; ++ ++struct hrtimer cec_key_timer; ++static int last_key_irq = -1; ++enum hrtimer_restart cec_key_up(struct hrtimer *timer) ++{ ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, cec_key_map[last_key_irq], 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ CEC_INFO("last:%d up\n", cec_key_map[last_key_irq]); ++ ++ return HRTIMER_NORESTART; ++} ++ ++void cec_user_control_pressed_irq(unsigned char message_irq) ++{ ++ if (message_irq < 160) { ++ CEC_INFO("Key pressed: %d\n", message_irq); ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, ++ cec_key_map[message_irq], 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ last_key_irq = message_irq; ++ hrtimer_start(&cec_key_timer, HR_DELAY(200), HRTIMER_MODE_REL); ++ CEC_INFO(":key map:%d\n", cec_key_map[message_irq]); ++ } ++} ++ ++void cec_user_control_released_irq(void) ++{ ++ /* ++ * key must be valid ++ */ ++ if (last_key_irq != -1) { ++ CEC_INFO("Key released: %d\n",last_key_irq); ++ hrtimer_cancel(&cec_key_timer); ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, cec_key_map[last_key_irq], 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ } ++} ++ ++void cec_set_reg_bits(unsigned int addr, unsigned int value, ++ unsigned int offset, unsigned int len) ++{ ++ unsigned int data32 = 0; ++ ++ data32 = readl(cec_dev->cec_reg + addr); ++ data32 &= ~(((1 << len) - 1) << offset); ++ data32 |= (value & ((1 << len) - 1)) << offset; ++ writel(data32, cec_dev->cec_reg + addr); ++} ++ ++unsigned int aocec_rd_reg(unsigned long addr) ++{ ++ unsigned int data32; ++ unsigned long flags; ++ waiting_aocec_free(); ++ spin_lock_irqsave(&cec_dev->cec_reg_lock, flags); ++ data32 = 0; ++ data32 |= 0 << 16; /* [16] cec_reg_wr */ ++ data32 |= 0 << 8; /* [15:8] cec_reg_wrdata */ ++ data32 |= addr << 0; /* [7:0] cec_reg_addr */ ++ writel(data32, cec_dev->cec_reg + AO_CEC_RW_REG); ++ ++ waiting_aocec_free(); ++ data32 = ((readl(cec_dev->cec_reg + AO_CEC_RW_REG)) >> 24) & 0xff; ++ spin_unlock_irqrestore(&cec_dev->cec_reg_lock, flags); ++ return data32; ++} /* aocec_rd_reg */ ++ ++void aocec_wr_reg(unsigned long addr, unsigned long data) ++{ ++ unsigned long data32; ++ unsigned long flags; ++ waiting_aocec_free(); ++ spin_lock_irqsave(&cec_dev->cec_reg_lock, flags); ++ data32 = 0; ++ data32 |= 1 << 16; /* [16] cec_reg_wr */ ++ data32 |= data << 8; /* [15:8] cec_reg_wrdata */ ++ data32 |= addr << 0; /* [7:0] cec_reg_addr */ ++ writel(data32, cec_dev->cec_reg + AO_CEC_RW_REG); ++ spin_unlock_irqrestore(&cec_dev->cec_reg_lock, flags); ++} /* aocec_wr_only_reg */ ++ ++static void cec_enable_irq(void) ++{ ++ cec_set_reg_bits(AO_CEC_INTR_MASKN, 0x6, 0, 3); ++ CEC_INFO("enable:int mask:0x%x\n", ++ readl(cec_dev->cec_reg + AO_CEC_INTR_MASKN)); ++} ++ ++static void cec_hw_buf_clear(void) ++{ ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_DISABLE); ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_ABORT); ++ aocec_wr_reg(CEC_RX_CLEAR_BUF, 1); ++ aocec_wr_reg(CEC_TX_CLEAR_BUF, 1); ++ udelay(100); ++ aocec_wr_reg(CEC_RX_CLEAR_BUF, 0); ++ aocec_wr_reg(CEC_TX_CLEAR_BUF, 0); ++ udelay(100); ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP); ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP); ++} ++ ++void cec_logicaddr_clear(void) ++{ ++ int i; ++ for (i = 0; i < 5; i++) { ++ aocec_wr_reg((CEC_LOGICAL_ADDR0 + i), 0); ++ cec_dev->cec_info.log_addr[i] = 0; ++ udelay(100); ++ } ++ cec_hw_buf_clear(); ++} ++ ++void cec_logicaddr_setByMask(unsigned int mask) ++{ ++ int i, j; ++ int reg = 0; ++ int primary = -1; ++ // ignore reserved device type ++ const int device_types[5] = {CEC_RECORDING_DEVICE, ++ CEC_PLAYBACK_DEVICE, ++ CEC_TUNER_DEVICE, ++ CEC_AUDIO_SYSTEM_DEVICE, ++ CEC_DISPLAY_DEVICE|CEC_UNREGISTERED ++ }; ++ mask &= 0xffff; ++ ++ if (mask == 0) { ++ cec_logicaddr_clear(); ++ return; ++ } ++ ++ for (i = CEC_TV_ADDR; i <= CEC_UNREGISTERED_ADDR; i++) { ++ if (reg > 4) { ++ break; ++ } ++ if ((mask & 1<cec_info.log_addr[reg] = i; ++ cec_logicaddr_set(i, (CEC_LOGICAL_ADDR0 + reg)); ++ if (primary == -1); ++ primary = i; ++ cec_logicaddr_config(primary, 1); ++ reg++; ++ break; ++ } ++ } ++ } ++ } ++} ++ ++void cec_logicaddr_set(int logicaddr, int logreg) ++{ ++ aocec_wr_reg(logreg, 0); ++ cec_hw_buf_clear(); ++ aocec_wr_reg(logreg, (logicaddr & 0xf)); ++ udelay(100); ++ aocec_wr_reg(logreg, (0x1 << 4) | (logicaddr & 0xf)); ++ if (cec_msg_dbg_en) ++ CEC_INFO("set logical addr:0x%x\n", ++ aocec_rd_reg(logreg)); ++} ++ ++static void cec_hw_reset(void) ++{ ++ writel(0x1, cec_dev->cec_reg + AO_CEC_GEN_CNTL); ++ /* Enable gated clock (Normal mode). */ ++ cec_set_reg_bits(AO_CEC_GEN_CNTL, 1, 1, 1); ++ /* Release SW reset */ ++ udelay(100); ++ cec_set_reg_bits(AO_CEC_GEN_CNTL, 0, 0, 1); ++ ++ /* Enable all AO_CEC interrupt sources */ ++ cec_set_reg_bits(AO_CEC_INTR_MASKN, 0x6, 0, 3); ++ ++ cec_logicaddr_set(cec_dev->cec_info.log_addr[0], CEC_LOGICAL_ADDR0); ++ ++ /* Cec arbitration 3/5/7 bit time set. */ ++ cec_arbit_bit_time_set(3, 0x118, 0); ++ cec_arbit_bit_time_set(5, 0x000, 0); ++ cec_arbit_bit_time_set(7, 0x2aa, 0); ++ ++ CEC_INFO("hw reset :logical addr:0x%x\n", ++ aocec_rd_reg(CEC_LOGICAL_ADDR0)); ++} ++ ++void cec_rx_buf_clear(void) ++{ ++ aocec_wr_reg(CEC_RX_CLEAR_BUF, 0x1); ++ aocec_wr_reg(CEC_RX_CLEAR_BUF, 0x0); ++ CEC_INFO("rx buf clean\n"); ++} ++ ++int cec_rx_buf_check(void) ++{ ++ unsigned int rx_num_msg = aocec_rd_reg(CEC_RX_NUM_MSG); ++ if (rx_num_msg) ++ CEC_INFO("rx msg num:0x%02x\n", rx_num_msg); ++ ++ return rx_num_msg; ++} ++ ++int cec_ll_rx(unsigned char *msg, unsigned char *len) ++{ ++ int i; ++ int ret = -1; ++ int pos; ++ int rx_stat; ++ ++ rx_stat = aocec_rd_reg(CEC_RX_MSG_STATUS); ++ if ((RX_DONE != rx_stat) || (1 != aocec_rd_reg(CEC_RX_NUM_MSG))) { ++ CEC_INFO("rx status:%x\n", rx_stat); ++ writel((1 << 2), cec_dev->cec_reg + AO_CEC_INTR_CLR); ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_ACK_CURRENT); ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP); ++ return ret; ++ } ++ ++ *len = aocec_rd_reg(CEC_RX_MSG_LENGTH) + 1; ++ ++ for (i = 0; i < (*len) && i < MAX_MSG; i++) ++ msg[i] = aocec_rd_reg(CEC_RX_MSG_0_HEADER + i); ++ ++ ret = rx_stat; ++ ++ /* ignore ping message */ ++ if (cec_msg_dbg_en == 1 && *len > 1) { ++ pos = 0; ++ pos += sprintf(msg_log_buf + pos, ++ "CEC: rx msg len: %d dat: ", *len); ++ for (i = 0; i < (*len); i++) ++ pos += sprintf(msg_log_buf + pos, "%02x ", msg[i]); ++ pos += sprintf(msg_log_buf + pos, "\n"); ++ msg_log_buf[pos] = '\0'; ++ CEC_INFO("%s", msg_log_buf); ++ } ++ writel((1 << 2), cec_dev->cec_reg + AO_CEC_INTR_CLR); ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_ACK_NEXT); ++ aocec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP); ++ return ret; ++} ++ ++void cec_polling_online_dev(int log_addr, int *bool) ++{ ++ unsigned int r; ++ unsigned char msg[1]; ++ int retry = 5; ++ ++ msg[0] = (log_addr<<4) | log_addr; ++ /* set broadcast address first */ ++ cec_logicaddr_set(0xf, CEC_LOGICAL_ADDR0); ++ if (cec_msg_dbg_en == 1) ++ CEC_INFO("CEC_LOGICAL_ADDR0:0x%i\n", ++ aocec_rd_reg(CEC_LOGICAL_ADDR0)); ++ while (retry) { ++ r = cec_ll_tx(msg, 1); ++ if (r == CEC_FAIL_BUSY) { ++ retry--; ++ CEC_INFO("try log addr %x busy, retry:%d\n", ++ log_addr, retry); ++ /* ++ * try to reset CEC if tx busy is found ++ */ ++ cec_hw_reset(); ++ } else ++ break; ++ } ++ ++ if (r == CEC_FAIL_NACK) { ++ *bool = 0; ++ } else if (r == CEC_FAIL_NONE) { ++ *bool = 1; ++ } ++ CEC_INFO("CEC: poll online logic device: 0x%x BOOL: %d\n", ++ log_addr, *bool); ++} ++ ++/************************ cec arbitration cts code **************************/ ++/* using the cec pin as fiq gpi to assist the bus arbitration */ ++ ++/* return value: 1: successful 0: error */ ++static int cec_ll_trigle_tx(const unsigned char *msg, int len) ++{ ++ int i; ++ unsigned int n; ++ int pos; ++ int reg; ++ unsigned int j = 20; ++ unsigned tx_stat; ++ static int cec_timeout_cnt = 1; ++ ++ while (1) { ++ tx_stat = aocec_rd_reg(CEC_TX_MSG_STATUS); ++ if (tx_stat != TX_BUSY) ++ break; ++ ++ if (!(j--)) { ++ CEC_INFO("wating busy timeout\n"); ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_ABORT); ++ cec_timeout_cnt++; ++ if (cec_timeout_cnt > 0x08) ++ cec_hw_reset(); ++ break; ++ } ++ msleep(20); ++ } ++ ++ reg = aocec_rd_reg(CEC_TX_MSG_STATUS); ++ if (reg == TX_IDLE || reg == TX_DONE) { ++ for (i = 0; i < len; i++) ++ aocec_wr_reg(CEC_TX_MSG_0_HEADER + i, msg[i]); ++ ++ aocec_wr_reg(CEC_TX_MSG_LENGTH, len-1); ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_REQ_CURRENT); ++ ++ if (cec_msg_dbg_en == 1) { ++ pos = 0; ++ pos += sprintf(msg_log_buf + pos, ++ "CEC: tx msg len: %d dat: ", len); ++ for (n = 0; n < len; n++) { ++ pos += sprintf(msg_log_buf + pos, ++ "%02x ", msg[n]); ++ } ++ ++ pos += sprintf(msg_log_buf + pos, "\n"); ++ ++ msg_log_buf[pos] = '\0'; ++ pr_info("%s", msg_log_buf); ++ } ++ cec_timeout_cnt = 0; ++ return 0; ++ } ++ return -1; ++} ++ ++void tx_irq_handle(void) ++{ ++ unsigned tx_status = aocec_rd_reg(CEC_TX_MSG_STATUS); ++ switch (tx_status) { ++ case TX_DONE: ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP); ++ cec_tx_result = 0; ++ complete(&cec_dev->tx_ok); ++ break; ++ ++ case TX_BUSY: ++ CEC_ERR("TX_BUSY\n"); ++ break; ++ ++ case TX_ERROR: ++ if (cec_msg_dbg_en == 1) ++ CEC_ERR("TX ERROR!!!\n"); ++ if (RX_ERROR == aocec_rd_reg(CEC_RX_MSG_STATUS)) { ++ cec_hw_reset(); ++ } else { ++ aocec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP); ++ } ++ cec_tx_result = 1; ++ complete(&cec_dev->tx_ok); ++ break; ++ ++ case TX_IDLE: ++ break; ++ default: ++ break; ++ } ++ writel((1 << 1), cec_dev->cec_reg + AO_CEC_INTR_CLR); ++} ++ ++/* Return value: < 0: fail, > 0: success */ ++int cec_ll_tx(const unsigned char *msg, unsigned char len) ++{ ++ int ret = 0; ++ int timeout = msecs_to_jiffies(1000); ++ ++ if (len == 0) ++ return CEC_FAIL_NONE; ++ ++ mutex_lock(&cec_dev->cec_mutex); ++ /* ++ * do not send messanges if tv is not support CEC ++ */ ++ ret = cec_ll_trigle_tx(msg, len); ++ if (ret < 0) { ++ /* we should increase send idx if busy */ ++ CEC_INFO("tx busy\n"); ++ mutex_unlock(&cec_dev->cec_mutex); ++ return CEC_FAIL_BUSY; ++ } ++ cec_tx_result = 0; ++ ret = wait_for_completion_timeout(&cec_dev->tx_ok, timeout); ++ if (ret <= 0) { ++ /* timeout or interrupt */ ++ ret = CEC_FAIL_OTHER; ++ CEC_INFO("tx timeout\n"); ++ } else { ++ if (cec_tx_result) ++ ret = CEC_FAIL_NACK; ++ else ++ ret = CEC_FAIL_NONE; ++ } ++ mutex_unlock(&cec_dev->cec_mutex); ++ ++ return ret; ++} ++ ++/* -------------------------------------------------------------------------- */ ++/* AO CEC0 config */ ++/* -------------------------------------------------------------------------- */ ++void ao_cec_init(void) ++{ ++ unsigned long data32; ++ unsigned int reg; ++ /* Assert SW reset AO_CEC */ ++ reg = readl(cec_dev->cec_reg + AO_CRT_CLK_CNTL1); ++ /* 24MHz/ (731 + 1) = 32786.885Hz */ ++ reg &= ~(0x7ff << 16); ++ reg |= (731 << 16); /* divider from 24MHz */ ++ reg |= (0x1 << 26); ++ reg &= ~(0x800 << 16); /* select divider */ ++ writel(reg, cec_dev->cec_reg + AO_CRT_CLK_CNTL1); ++ data32 = 0; ++ data32 |= 0 << 1; /* [2:1] cntl_clk: */ ++ /* 0=Disable clk (Power-off mode); */ ++ /* 1=Enable gated clock (Normal mode); */ ++ /* 2=Enable free-run clk (Debug mode). */ ++ data32 |= 1 << 0; /* [0] sw_reset: 1=Reset */ ++ writel(data32, cec_dev->cec_reg + AO_CEC_GEN_CNTL); ++ /* Enable gated clock (Normal mode). */ ++ cec_set_reg_bits(AO_CEC_GEN_CNTL, 1, 1, 1); ++ /* Release SW reset */ ++ cec_set_reg_bits(AO_CEC_GEN_CNTL, 0, 0, 1); ++ ++ /* Enable all AO_CEC interrupt sources */ ++ cec_enable_irq(); ++} ++ ++void cec_arbit_bit_time_set(unsigned bit_set, unsigned time_set, unsigned flag) ++{ /* 11bit:bit[10:0] */ ++ if (flag) { ++ CEC_INFO("bit_set:0x%x;time_set:0x%x\n", ++ bit_set, time_set); ++ } ++ ++ switch (bit_set) { ++ case 3: ++ /* 3 bit */ ++ if (flag) { ++ CEC_INFO("read 3 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_4BIT_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_4BIT_BIT7_0)); ++ } ++ aocec_wr_reg(AO_CEC_TXTIME_4BIT_BIT7_0, time_set & 0xff); ++ aocec_wr_reg(AO_CEC_TXTIME_4BIT_BIT10_8, (time_set >> 8) & 0x7); ++ if (flag) { ++ CEC_INFO("write 3 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_4BIT_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_4BIT_BIT7_0)); ++ } ++ break; ++ /* 5 bit */ ++ case 5: ++ if (flag) { ++ CEC_INFO("read 5 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_2BIT_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_2BIT_BIT7_0)); ++ } ++ aocec_wr_reg(AO_CEC_TXTIME_2BIT_BIT7_0, time_set & 0xff); ++ aocec_wr_reg(AO_CEC_TXTIME_2BIT_BIT10_8, (time_set >> 8) & 0x7); ++ if (flag) { ++ CEC_INFO("write 5 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_2BIT_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_2BIT_BIT7_0)); ++ } ++ break; ++ /* 7 bit */ ++ case 7: ++ if (flag) { ++ CEC_INFO("read 7 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_17MS_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_17MS_BIT7_0)); ++ } ++ aocec_wr_reg(AO_CEC_TXTIME_17MS_BIT7_0, time_set & 0xff); ++ aocec_wr_reg(AO_CEC_TXTIME_17MS_BIT10_8, (time_set >> 8) & 0x7); ++ if (flag) { ++ CEC_INFO("write 7 bit:0x%x%x\n", ++ aocec_rd_reg(AO_CEC_TXTIME_17MS_BIT10_8), ++ aocec_rd_reg(AO_CEC_TXTIME_17MS_BIT7_0)); ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++static unsigned int ao_cec_intr_stat(void) ++{ ++ return readl(cec_dev->cec_reg + AO_CEC_INTR_STAT); ++} ++ ++unsigned int cec_intr_stat(void) ++{ ++ return ao_cec_intr_stat(); ++} ++ ++/* ++ *wr_flag: 1 write; value valid ++ * 0 read; value invalid ++ */ ++unsigned int cec_config(unsigned int value, bool wr_flag) ++{ ++ if (wr_flag) ++ cec_set_reg_bits(AO_DEBUG_REG0, value, 0, 8); ++ ++ return readl(cec_dev->cec_reg + AO_DEBUG_REG0); ++} ++ ++/* ++ *wr_flag:1 write; value valid ++ * 0 read; value invalid ++ */ ++unsigned int cec_phyaddr_config(unsigned int value, bool wr_flag) ++{ ++ if (wr_flag) ++ cec_set_reg_bits(AO_DEBUG_REG1, value, 0, 16); ++ ++ return readl(cec_dev->cec_reg + AO_DEBUG_REG1); ++} ++ ++/* ++ *wr_flag:1 write; value valid ++ * 0 read; value invalid ++ */ ++unsigned int cec_logicaddr_config(unsigned int value, bool wr_flag) ++{ ++ if (wr_flag) ++ cec_set_reg_bits(AO_DEBUG_REG3, value, 0, 8); ++ ++ return readl(cec_dev->cec_reg + AO_DEBUG_REG3); ++} ++ ++void cec_keep_reset(void) ++{ ++ writel(0x1, cec_dev->cec_reg + AO_CEC_GEN_CNTL); ++} ++/* ++ * cec hw module init befor allocate logical address ++ */ ++static void cec_pre_init(void) ++{ ++ ao_cec_init(); ++ ++ cec_arbit_bit_time_set(3, 0x118, 0); ++ cec_arbit_bit_time_set(5, 0x000, 0); ++ cec_arbit_bit_time_set(7, 0x2aa, 0); ++} ++ ++static int cec_late_check_rx_buffer(void) ++{ ++ int ret; ++ struct delayed_work *dwork = &cec_dev->cec_work; ++ ++ ret = cec_rx_buf_check(); ++ if (!ret) ++ return 0; ++ /* ++ * start another check if rx buffer is full ++ */ ++ if ((-1) == cec_ll_rx(rx_msg, &rx_len)) { ++ CEC_INFO("buffer got unrecorgnized msg\n"); ++ cec_rx_buf_clear(); ++ return 0; ++ } else { ++ mod_delayed_work(cec_dev->cec_thread, dwork, 0); ++ return 1; ++ } ++} ++ ++void cec_key_report(int suspend) ++{ ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_POWER, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_POWER, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ if (!suspend) ++ CEC_INFO("== WAKE UP BY CEC ==\n") ++ else ++ CEC_INFO("== SLEEP by CEC==\n") ++} ++ ++void cec_give_version(unsigned int dest) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[3]; ++ ++ if (0xf != dest) { ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_CEC_VERSION; ++ msg[2] = CEC_VERSION_14A; ++ cec_ll_tx(msg, 3); ++ } ++} ++ ++void cec_report_physical_address_smp(void) ++{ ++ unsigned char msg[5]; ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char phy_addr_ab, phy_addr_cd; ++ ++ phy_addr_ab = (cec_dev->phy_addr >> 8) & 0xff; ++ phy_addr_cd = (cec_dev->phy_addr >> 0) & 0xff; ++ msg[0] = ((index & 0xf) << 4) | CEC_BROADCAST_ADDR; ++ msg[1] = CEC_OC_REPORT_PHYSICAL_ADDRESS; ++ msg[2] = phy_addr_ab; ++ msg[3] = phy_addr_cd; ++ msg[4] = cec_dev->dev_type; ++ ++ cec_ll_tx(msg, 5); ++} ++ ++void cec_device_vendor_id(void) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[5]; ++ unsigned int vendor_id; ++ ++ vendor_id = cec_dev->v_data.vendor_id; ++ msg[0] = ((index & 0xf) << 4) | CEC_BROADCAST_ADDR; ++ msg[1] = CEC_OC_DEVICE_VENDOR_ID; ++ msg[2] = (vendor_id >> 16) & 0xff; ++ msg[3] = (vendor_id >> 8) & 0xff; ++ msg[4] = (vendor_id >> 0) & 0xff; ++ ++ cec_ll_tx(msg, 5); ++} ++ ++void cec_give_deck_status(unsigned int dest) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[3]; ++ ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_DECK_STATUS; ++ msg[2] = 0x1a; ++ cec_ll_tx(msg, 3); ++} ++ ++void cec_menu_status_smp(int dest, int status) ++{ ++ unsigned char msg[3]; ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_MENU_STATUS; ++ if (status == DEVICE_MENU_ACTIVE) ++ msg[2] = DEVICE_MENU_ACTIVE; ++ else ++ msg[2] = DEVICE_MENU_INACTIVE; ++ cec_ll_tx(msg, 3); ++} ++ ++void cec_inactive_source(int dest) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[4]; ++ unsigned char phy_addr_ab, phy_addr_cd; ++ ++ phy_addr_ab = (cec_dev->phy_addr >> 8) & 0xff; ++ phy_addr_cd = (cec_dev->phy_addr >> 0) & 0xff; ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_INACTIVE_SOURCE; ++ msg[2] = phy_addr_ab; ++ msg[3] = phy_addr_cd; ++ ++ cec_ll_tx(msg, 4); ++} ++ ++void cec_set_osd_name(int dest) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char osd_len = strlen(cec_dev->cec_info.osd_name); ++ unsigned char msg[16]; ++ ++ if (0xf != dest) { ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_SET_OSD_NAME; ++ memcpy(&msg[2], cec_dev->cec_info.osd_name, osd_len); ++ ++ cec_ll_tx(msg, 2 + osd_len); ++ } ++} ++ ++void cec_active_source_smp(void) ++{ ++ unsigned char msg[4]; ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char phy_addr_ab; ++ unsigned char phy_addr_cd; ++ ++ phy_addr_ab = (cec_dev->phy_addr >> 8) & 0xff; ++ phy_addr_cd = (cec_dev->phy_addr >> 0) & 0xff; ++ msg[0] = ((index & 0xf) << 4) | CEC_BROADCAST_ADDR; ++ msg[1] = CEC_OC_ACTIVE_SOURCE; ++ msg[2] = phy_addr_ab; ++ msg[3] = phy_addr_cd; ++ cec_ll_tx(msg, 4); ++} ++ ++void cec_set_stream_path(unsigned char *msg) ++{ ++ unsigned int phy_addr_active; ++ ++ phy_addr_active = (unsigned int)(msg[2] << 8 | msg[3]); ++ if (phy_addr_active == cec_dev->phy_addr) { ++ cec_active_source_smp(); ++ /* ++ * some types of TV such as panasonic need to send menu status, ++ * otherwise it will not send remote key event to control ++ * device's menu ++ */ ++ cec_menu_status_smp(msg[0] >> 4, DEVICE_MENU_ACTIVE); ++ } ++} ++ ++void cec_report_power_status(int dest, int status) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[3]; ++ ++ msg[0] = ((index & 0xf) << 4) | dest; ++ msg[1] = CEC_OC_REPORT_POWER_STATUS; ++ msg[2] = status; ++ cec_ll_tx(msg, 3); ++} ++ ++void cec_send_simplink_alive(void) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[4]; ++ ++ msg[0] = ((index & 0xf) << 4) | CEC_TV_ADDR; ++ msg[1] = CEC_OC_VENDOR_COMMAND; ++ msg[2] = 0x2; ++ msg[3] = 0x5; ++ ++ cec_ll_tx(msg, 4); ++} ++ ++void cec_send_simplink_ack(void) ++{ ++ unsigned char index = cec_dev->cec_info.log_addr[0]; ++ unsigned char msg[4]; ++ ++ msg[0] = ((index & 0xf) << 4) | CEC_TV_ADDR; ++ msg[1] = CEC_OC_VENDOR_COMMAND; ++ msg[2] = 0x5; ++ msg[3] = 0x1; ++ ++ cec_ll_tx(msg, 4); ++} ++ ++int cec_node_init(struct hdmitx_dev *hdmitx_device) ++{ ++ unsigned char a, b, c, d; ++ ++ int i, bool = 0; ++ int phy_addr_ok = 1; ++ const enum _cec_log_dev_addr_e player_dev[3] = { ++ CEC_RECORDING_DEVICE_1_ADDR, ++ CEC_RECORDING_DEVICE_2_ADDR, ++ CEC_RECORDING_DEVICE_3_ADDR, ++ }; ++ ++ unsigned long cec_phy_addr; ++ ++ /* If no connect, return directly */ ++ if ((hdmitx_device->cec_init_ready == 0) || ++ (hdmitx_device->hpd_state == 0)) { ++ return -1; ++ } ++ a = hdmitx_device->hdmi_info.vsdb_phy_addr.a; ++ b = hdmitx_device->hdmi_info.vsdb_phy_addr.b; ++ c = hdmitx_device->hdmi_info.vsdb_phy_addr.c; ++ d = hdmitx_device->hdmi_info.vsdb_phy_addr.d; ++ CEC_INFO("cec_node_init started\n"); ++ ++ cec_phy_addr = ((a << 12) | (b << 8) | (c << 4) | (d << 0)); ++ ++ for (i = 0; i < 3; i++) { ++ CEC_INFO("CEC: start poll dev\n"); ++ cec_polling_online_dev(player_dev[i], &bool); ++ CEC_INFO("player_dev[%d]:0x%x\n", i, player_dev[i]); ++ if (bool == 0) { /* 0 means that no any respond */ ++ /* If VSDB is not valid, use last or default physical address. */ ++ cec_logicaddr_set(player_dev[i], CEC_LOGICAL_ADDR0); ++ if (hdmitx_device->hdmi_info.vsdb_phy_addr.valid == 0) { ++ phy_addr_ok = 0; ++ CEC_INFO("invalid cec PhyAddr\n"); ++ if (cec_phyaddr_config(0, 0)) { ++ CEC_INFO("use last physical address\n"); ++ } else { ++ cec_phyaddr_config(0x2000, 1); ++ CEC_INFO("use Def Phy address\n"); ++ } ++ } else ++ cec_phyaddr_config(cec_phy_addr, 1); ++ ++ CEC_INFO("physical address:0x%x\n", ++ cec_phyaddr_config(0, 0)); ++ ++ cec_dev->cec_info.power_status = TRANS_STANDBY_TO_ON; ++ cec_logicaddr_config(player_dev[i], 1); ++ cec_dev->cec_info.log_addr[0] = player_dev[i]; ++ /* Set Physical address */ ++ cec_dev->phy_addr = cec_phy_addr; ++ ++ cec_dev->cec_info.cec_version = CEC_VERSION_14A; ++ cec_dev->cec_info.vendor_id = cec_dev->v_data.vendor_id; ++ strcpy(cec_dev->cec_info.osd_name, ++ cec_dev->v_data.cec_osd_string); ++ cec_logicaddr_set(player_dev[i], CEC_LOGICAL_ADDR0); ++ ++ CEC_INFO("Set logical address: %d\n", ++ player_dev[i]); ++ ++ cec_dev->cec_info.power_status = POWER_ON; ++ if (cec_dev->cec_info.menu_status == DEVICE_MENU_INACTIVE) ++ break; ++ msleep(100); ++ if (phy_addr_ok) { ++ cec_report_physical_address_smp(); ++ msleep(150); ++ } ++ cec_device_vendor_id(); ++ cec_set_osd_name(0); ++ cec_active_source_smp(); ++ ++ cec_menu_status_smp(player_dev[i], DEVICE_MENU_ACTIVE); ++ ++ msleep(100); ++ cec_dev->cec_info.menu_status = DEVICE_MENU_ACTIVE; ++ break; ++ } ++ } ++ if (bool == 1) { ++ CEC_INFO("Can't get a valid logical address\n"); ++ return -1; ++ } else { ++ CEC_INFO("cec node init: cec features ok !\n"); ++ return 0; ++ } ++} ++ ++static void cec_rx_process(void) ++{ ++ int len = rx_len; ++ int initiator, follower; ++ int opcode; ++ unsigned char msg[MAX_MSG] = {}; ++ ++ if (len < 2 || !new_msg) /* ignore ping message */ ++ return; ++ ++ memcpy(msg, rx_msg, len); ++ initiator = ((msg[0] >> 4) & 0xf); ++ follower = msg[0] & 0xf; ++ if (follower != 0xf && follower != cec_dev->cec_info.log_addr[0]) { ++ CEC_ERR("wrong rx message of bad follower:%x", follower); ++ return; ++ } ++ opcode = msg[1]; ++ switch (opcode) { ++ case CEC_OC_GET_CEC_VERSION: ++ cec_give_version(initiator); ++ break; ++ ++ case CEC_OC_GIVE_DECK_STATUS: ++ cec_give_deck_status(initiator); ++ break; ++ ++ case CEC_OC_GIVE_PHYSICAL_ADDRESS: ++ cec_report_physical_address_smp(); ++ break; ++ ++ case CEC_OC_GIVE_DEVICE_VENDOR_ID: ++ cec_device_vendor_id(); ++ break; ++ ++ case CEC_OC_GIVE_OSD_NAME: ++ cec_set_osd_name(initiator); ++ break; ++ ++ case CEC_OC_STANDBY: ++ cec_inactive_source(initiator); ++ cec_menu_status_smp(initiator, DEVICE_MENU_INACTIVE); ++ break; ++ ++ case CEC_OC_SET_STREAM_PATH: ++ cec_set_stream_path(msg); ++ /* wake up if in early suspend */ ++ if (cec_dev->cec_suspend == CEC_EARLY_SUSPEND) ++ cec_key_report(0); ++ break; ++ ++ case CEC_OC_REQUEST_ACTIVE_SOURCE: ++ if (!cec_dev->cec_suspend) ++ cec_active_source_smp(); ++ break; ++ ++ case CEC_OC_GIVE_DEVICE_POWER_STATUS: ++ if (cec_dev->cec_suspend) ++ cec_report_power_status(initiator, POWER_STANDBY); ++ else ++ cec_report_power_status(initiator, POWER_ON); ++ break; ++ ++ ++ case CEC_OC_USER_CONTROL_RELEASED: ++ cec_user_control_released_irq(); ++ break; ++ ++ case CEC_OC_USER_CONTROL_PRESSED: ++ if (len < 3) ++ break; ++ cec_user_control_pressed_irq(msg[2]); ++ /* wake up by key function */ ++ if (cec_dev->cec_suspend == CEC_EARLY_SUSPEND) { ++ if (msg[2] == 0x40 || msg[2] == 0x6d) ++ cec_key_report(0); ++ } ++ break; ++ ++ case CEC_OC_PLAY: ++ if (len < 3) ++ break; ++ switch (msg[2]) { ++ case 0x24: ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_PLAYPAUSE, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_PLAYPAUSE, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ break; ++ case 0x25: ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_PLAYPAUSE, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_PLAYPAUSE, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ case CEC_OC_DECK_CONTROL: ++ if (len < 3) ++ break; ++ switch (msg[2]) { ++ case 0x3: ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_STOP, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, ++ EV_KEY, KEY_STOP, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ case CEC_OC_VENDOR_REMOTE_BUTTON_DOWN: ++ if (len < 3) ++ break; ++ switch(msg[2]){ ++ //samsung vendor keys ++ case 0x91: ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_EXIT, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_EXIT, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ break; ++ case 0x96: ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_LIST, 1); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ input_event(cec_dev->cec_info.remote_cec_dev, EV_KEY, KEY_LIST, 0); ++ input_sync(cec_dev->cec_info.remote_cec_dev); ++ break; ++ default: ++ break; ++ } ++ break; ++ case CEC_OC_MENU_REQUEST: ++ if (cec_dev->cec_suspend) ++ cec_menu_status_smp(initiator, DEVICE_MENU_INACTIVE); ++ else ++ cec_menu_status_smp(initiator, DEVICE_MENU_ACTIVE); ++ break; ++ case CEC_OC_VENDOR_COMMAND: ++ if (len < 3) ++ break; ++ if (msg[2] == 0x1) { ++ cec_report_power_status(initiator, cec_dev->cec_info.power_status); ++ cec_send_simplink_alive(); ++ } else if (msg[2] == 0x4) { ++ cec_send_simplink_ack(); ++ } ++ break; ++ ++ default: ++ CEC_ERR("unsupported command:%x\n", opcode); ++ break; ++ } ++ new_msg = 0; ++} ++ ++static void cec_task(struct work_struct *work) ++{ ++ struct delayed_work *dwork; ++ int ret; ++ ++ dwork = &cec_dev->cec_work; ++ if (cec_dev && ++ !(cec_dev->hal_flag & (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL))) { ++ if (1 << cec_dev->cec_info.log_addr[0] & (1 << 0x0 | 1 << 0xF)) { ++ ret = cec_node_init(cec_dev->tx_dev); ++ if (ret < 0) { ++ return; ++ } ++ } ++ cec_rx_process(); ++ } ++ if (!cec_late_check_rx_buffer()) ++ queue_delayed_work(cec_dev->cec_thread, dwork, CEC_FRAME_DELAY); ++} ++ ++irqreturn_t cec_isr_handler(int irq, void *dev_instance) ++{ ++ unsigned int intr_stat = 0; ++ struct delayed_work *dwork; ++ ++ dwork = &cec_dev->cec_work; ++ intr_stat = cec_intr_stat(); ++ if (intr_stat & (1<<1)) { /* aocec tx intr */ ++ tx_irq_handle(); ++ return IRQ_HANDLED; ++ } ++ if ((-1) == cec_ll_rx(rx_msg, &rx_len)) ++ return IRQ_HANDLED; ++ ++ complete(&cec_dev->rx_ok); ++ /* check rx buffer is full */ ++ new_msg = 1; ++ mod_delayed_work(cec_dev->cec_thread, dwork, 0); ++ return IRQ_HANDLED; ++} ++ ++/******************** cec class interface *************************/ ++static ssize_t device_type_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%ld\n", cec_dev->dev_type); ++} ++ ++static ssize_t device_type_store(struct class *cla, ++ struct class_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned long type; ++ ++ if (sscanf(buf, "%ld", &type) != 1) ++ return -EINVAL; ++ ++ cec_dev->dev_type = type; ++ CEC_ERR("set dev_type to %ld\n", type); ++ return count; ++} ++ ++static ssize_t menu_language_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ char a, b, c; ++ a = ((cec_dev->cec_info.menu_lang >> 16) & 0xff); ++ b = ((cec_dev->cec_info.menu_lang >> 8) & 0xff); ++ c = ((cec_dev->cec_info.menu_lang >> 0) & 0xff); ++ return sprintf(buf, "%c%c%c\n", a, b, c); ++} ++ ++static ssize_t menu_language_store(struct class *cla, ++ struct class_attribute *attr, const char *buf, size_t count) ++{ ++ char a, b, c; ++ ++ if (sscanf(buf, "%c%c%c", &a, &b, &c) != 3) ++ return -EINVAL; ++ ++ cec_dev->cec_info.menu_lang = (a << 16) | (b << 8) | c; ++ CEC_ERR("set menu_language to %s\n", buf); ++ return count; ++} ++ ++static ssize_t vendor_id_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%x\n", cec_dev->cec_info.vendor_id); ++} ++ ++static ssize_t vendor_id_store(struct class *cla, struct class_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int id; ++ ++ if (sscanf(buf, "%x", &id) != 1) ++ return -EINVAL; ++ cec_dev->cec_info.vendor_id = id; ++ return count; ++} ++ ++static ssize_t port_num_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", cec_dev->port_num); ++} ++ ++static ssize_t arc_port_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%x\n", cec_dev->arc_port); ++} ++ ++static ssize_t osd_name_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%s\n", cec_dev->cec_info.osd_name); ++} ++ ++static ssize_t port_seq_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%x\n", cec_dev->port_seq); ++} ++ ++static ssize_t port_status_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ unsigned int tmp = 0; ++ ++ if (cec_dev->dev_type == DEV_TYPE_TX) { ++ tmp = cec_dev->tx_dev->hpd_state; ++ return sprintf(buf, "%x\n", tmp); ++ } ++ else { ++#ifdef CONFIG_TVIN_HDMI ++ tmp = hdmirx_rd_top(TOP_HPD_PWR5V); ++ CEC_INFO("TOP_HPD_PWR5V:%x\n", tmp); ++ tmp >>= 20; ++ tmp &= 0xf; ++#endif ++ return sprintf(buf, "%x\n", tmp); ++ } ++} ++ ++static ssize_t physical_addr_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ unsigned int tmp = cec_dev->phy_addr; ++ ++ return sprintf(buf, "%04x\n", tmp); ++} ++ ++static ssize_t physical_addr_store(struct class *cla, ++ struct class_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int addr; ++ ++ if (sscanf(buf, "%x", &addr) != 1) ++ return -EINVAL; ++ ++ if (addr > 0xffff || addr < 0) { ++ CEC_ERR("invalid input:%s\n", buf); ++ phy_addr_test = 0; ++ return -EINVAL; ++ } ++ cec_dev->phy_addr = addr; ++ phy_addr_test = 1; ++ return count; ++} ++ ++static ssize_t dbg_en_show(struct class *cla, ++ struct class_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%x\n", cec_msg_dbg_en); ++} ++ ++static ssize_t dbg_en_store(struct class *cla, struct class_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int en; ++ ++ if (sscanf(buf, "%d", &en) != 1) ++ return -EINVAL; ++ ++ cec_msg_dbg_en = en ? 1 : 0; ++ return count; ++} ++ ++static struct class_attribute aocec_class_attr[] = { ++ __ATTR_RO(port_num), ++ __ATTR_RO(port_seq), ++ __ATTR_RO(osd_name), ++ __ATTR_RO(port_status), ++ __ATTR_RO(arc_port), ++ __ATTR(physical_addr, 0664, physical_addr_show, physical_addr_store), ++ __ATTR(vendor_id, 0664, vendor_id_show, vendor_id_store), ++ __ATTR(menu_language, 0664, menu_language_show, menu_language_store), ++ __ATTR(device_type, 0664, device_type_show, device_type_store), ++ __ATTR(dbg_en, 0664, dbg_en_show, dbg_en_store), ++ __ATTR_NULL ++}; ++ ++/******************** cec hal interface ***************************/ ++static int hdmitx_cec_open(struct inode *inode, struct file *file) ++{ ++ cec_dev->cec_info.open_count++; ++ if (cec_dev->cec_info.open_count) { ++ cec_dev->cec_info.hal_ctl = 1; ++ /* enable all cec features */ ++ cec_config(0x2f, 1); ++ } ++ return 0; ++} ++ ++static int hdmitx_cec_release(struct inode *inode, struct file *file) ++{ ++ cec_dev->cec_info.open_count--; ++ if (!cec_dev->cec_info.open_count) { ++ cec_dev->cec_info.hal_ctl = 0; ++ /* disable all cec features */ ++ cec_config(0x0, 1); ++ } ++ return 0; ++} ++ ++static ssize_t hdmitx_cec_read(struct file *f, char __user *buf, ++ size_t size, loff_t *p) ++{ ++ int ret; ++ ++ if ((cec_dev->hal_flag & (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL))) ++ rx_len = 0; ++ ret = wait_for_completion_timeout(&cec_dev->rx_ok, CEC_FRAME_DELAY); ++ if (ret <= 0) ++ return ret; ++ if (rx_len == 0) ++ return 0; ++ ++ if (copy_to_user(buf, rx_msg, rx_len)) ++ return -EINVAL; ++ return rx_len; ++} ++ ++static ssize_t hdmitx_cec_write(struct file *f, const char __user *buf, ++ size_t size, loff_t *p) ++{ ++ unsigned char tempbuf[16] = {}; ++ int ret; ++ ++ if (size > 16) ++ size = 16; ++ if (size <= 0) ++ return 0; ++ ++ if (copy_from_user(tempbuf, buf, size)) ++ return -EINVAL; ++ ++ ret = cec_ll_tx(tempbuf, size); ++ /* delay a byte for continue message send */ ++ msleep(25); ++ if (ret == CEC_FAIL_NACK) { ++ return -1; ++ } ++ else { ++ return size; ++ } ++} ++ ++static void init_cec_port_info(struct hdmi_port_info *port, ++ struct ao_cec_dev *cec_dev) ++{ ++ unsigned int a, b, c; ++ ++ b = cec_dev->port_num; ++ for (a = 0; a < b; a++) { ++ port[a].type = HDMI_INPUT; ++ port[a].port_id = a + 1; ++ port[a].cec_supported = 1; ++ /* set ARC feature according mask */ ++ if (cec_dev->arc_port & (1 << a)) ++ port[a].arc_supported = 1; ++ else ++ port[a].arc_supported = 0; ++ ++ /* set port physical address according port sequence */ ++ if (cec_dev->port_seq) { ++ c = (cec_dev->port_seq >> (4 * a)) & 0xf; ++ port[a].physical_address = (c + 1) * 0x1000; ++ } else { ++ /* asending order if port_seq is not set */ ++ port[a].physical_address = (a + 1) * 0x1000; ++ } ++ } ++} ++ ++static long hdmitx_cec_ioctl(struct file *f, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = (void __user *)arg; ++ unsigned long tmp; ++ struct hdmi_port_info *port; ++ int a, b, c, d; ++ ++ switch (cmd) { ++ case CEC_IOC_GET_PHYSICAL_ADDR: ++ if (cec_dev->dev_type == DEV_TYPE_TX && !phy_addr_test) { ++ /* physical address for mbox */ ++ if (cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.valid == 0) ++ return -EINVAL; ++ a = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.a; ++ b = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.b; ++ c = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.c; ++ d = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.d; ++ tmp = ((a << 12) | (b << 8) | (c << 4) | (d << 0)); ++ } else { ++ /* physical address for TV */ ++ tmp = 0; ++ } ++ if (!phy_addr_test) { ++ cec_dev->phy_addr = tmp; ++ cec_phyaddr_config(tmp, 1); ++ } else ++ tmp = cec_dev->phy_addr; ++ ++ if (copy_to_user(argp, &tmp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ break; ++ ++ case CEC_IOC_GET_VERSION: ++ tmp = CEC_VERSION_14A; ++ if (copy_to_user(argp, &tmp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ break; ++ ++ case CEC_IOC_GET_VENDOR_ID: ++ tmp = cec_dev->v_data.vendor_id; ++ if (copy_to_user(argp, &tmp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ break; ++ ++ case CEC_IOC_GET_PORT_NUM: ++ tmp = cec_dev->port_num; ++ if (copy_to_user(argp, &tmp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ break; ++ ++ case CEC_IOC_GET_PORT_INFO: ++ port = kzalloc(sizeof(*port) * cec_dev->port_num, GFP_KERNEL); ++ if (!port) { ++ CEC_ERR("no memory\n"); ++ return -EINVAL; ++ } ++ if (cec_dev->dev_type == DEV_TYPE_TX) { ++ /* for tx only 1 port */ ++ a = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.a; ++ b = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.b; ++ c = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.c; ++ d = cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.d; ++ tmp = ((a << 12) | (b << 8) | (c << 4) | (d << 0)); ++ if (cec_dev->tx_dev->hdmi_info.vsdb_phy_addr.valid == 0) ++ tmp = 0xffff; ++ port->type = HDMI_OUTPUT; ++ port->port_id = 1; ++ port->cec_supported = 1; ++ /* not support arc for tx */ ++ port->arc_supported = 0; ++ port->physical_address = tmp & 0xffff; ++ if (copy_to_user(argp, port, sizeof(*port))) { ++ kfree(port); ++ return -EINVAL; ++ } ++ } else { ++ b = cec_dev->port_num; ++ init_cec_port_info(port, cec_dev); ++ if (copy_to_user(argp, port, sizeof(*port) * b)) { ++ kfree(port); ++ return -EINVAL; ++ } ++ } ++ kfree(port); ++ break; ++ ++ case CEC_IOC_SET_OPTION_WAKEUP: ++ /* TODO: */ ++ break; ++ ++ case CEC_IOC_SET_OPTION_ENALBE_CEC: ++ tmp = (1 << HDMI_OPTION_ENABLE_CEC); ++ if (arg) { ++ cec_dev->hal_flag |= tmp; ++ cec_pre_init(); ++ } else { ++ cec_dev->hal_flag &= ~(tmp); ++ CEC_INFO("disable CEC\n"); ++ cec_keep_reset(); ++ } ++ break; ++ ++ case CEC_IOC_SET_OPTION_SYS_CTRL: ++ tmp = (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL); ++ if (arg) ++ cec_dev->hal_flag |= tmp; ++ else ++ cec_dev->hal_flag &= ~(tmp); ++ break; ++ ++ case CEC_IOC_SET_OPTION_SET_LANG: ++ cec_dev->cec_info.menu_lang = arg; ++ break; ++ ++ case CEC_IOC_GET_CONNECT_STATUS: ++ if (cec_dev->dev_type == DEV_TYPE_TX) ++ tmp = cec_dev->tx_dev->hpd_state; ++ #ifdef CONFIG_TVIN_HDMI ++ else { ++ if (copy_from_user(&a, argp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ tmp = (hdmirx_rd_top(TOP_HPD_PWR5V) >> 20); ++ if (tmp & (1 << (a - 1))) ++ tmp = 1; ++ else ++ tmp = 0; ++ } ++ #endif ++ if (copy_to_user(argp, &tmp, _IOC_SIZE(cmd))) ++ return -EINVAL; ++ break; ++ ++ case CEC_IOC_ADD_LOGICAL_ADDR: ++ tmp = arg & 0xf; ++ CEC_INFO("CEC LA ARG:%ld", arg); ++ cec_logicaddr_set(tmp, CEC_LOGICAL_ADDR0); ++ cec_dev->cec_info.log_addr[0] = tmp; ++ //cec_logicaddr_setByMask(tmp); ++ /* add by hal, to init some data structure */ ++ cec_dev->cec_info.power_status = POWER_ON; ++ cec_logicaddr_config(tmp, 1); ++ ++ cec_dev->cec_info.cec_version = CEC_VERSION_14A; ++ cec_dev->cec_info.vendor_id = cec_dev->v_data.vendor_id; ++ strcpy(cec_dev->cec_info.osd_name, ++ cec_dev->v_data.cec_osd_string); ++ ++ if (cec_dev->dev_type == DEV_TYPE_TX) ++ cec_dev->cec_info.menu_status = DEVICE_MENU_ACTIVE; ++ break; ++ ++ case CEC_IOC_CLR_LOGICAL_ADDR: ++ /* TODO: clear global info */ ++ cec_logicaddr_clear(); ++ break; ++ ++ case CEC_IOC_SET_DEV_TYPE: ++ if (arg != DEV_TYPE_TX && arg != DEV_TYPE_RX) ++ return -EINVAL; ++ cec_dev->dev_type = arg; ++ break; ++ ++ case CEC_IOC_SET_ARC_ENABLE: ++ #ifdef CONFIG_TVIN_HDMI ++ /* select arc according arg */ ++ if (arg) ++ hdmirx_wr_top(TOP_ARCTX_CNTL, 0x03); ++ else ++ hdmirx_wr_top(TOP_ARCTX_CNTL, 0x00); ++ CEC_INFO("set arc en:%ld, reg:%lx\n", ++ arg, hdmirx_rd_top(TOP_ARCTX_CNTL)); ++ #endif ++ break; ++ ++ default: ++ break; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_COMPAT ++static long hdmitx_cec_compat_ioctl(struct file *f, ++ unsigned int cmd, unsigned long arg) ++{ ++ arg = (unsigned long)compat_ptr(arg); ++ return hdmitx_cec_ioctl(f, cmd, arg); ++} ++#endif ++ ++/* for improve rw permission */ ++static char *aml_cec_class_devnode(struct device *dev, umode_t *mode) ++{ ++ if (mode) ++ *mode = 0666; ++ CEC_INFO("mode is %x\n", *mode); ++ return NULL; ++} ++ ++static struct class aocec_class = { ++ .name = CEC_DEV_NAME, ++ .class_attrs = aocec_class_attr, ++ .devnode = aml_cec_class_devnode, ++}; ++ ++ ++static const struct file_operations hdmitx_cec_fops = { ++ .owner = THIS_MODULE, ++ .open = hdmitx_cec_open, ++ .read = hdmitx_cec_read, ++ .write = hdmitx_cec_write, ++ .release = hdmitx_cec_release, ++ .unlocked_ioctl = hdmitx_cec_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = hdmitx_cec_compat_ioctl, ++#endif ++}; ++ ++/************************ cec high level code *****************************/ ++#ifdef CONFIG_HAS_EARLYSUSPEND ++static void aocec_early_suspend(struct early_suspend *h) ++{ ++ cec_dev->cec_suspend = CEC_EARLY_SUSPEND; ++ CEC_INFO("%s, suspend:%d\n", __func__, cec_dev->cec_suspend); ++} ++ ++static void aocec_late_resume(struct early_suspend *h) ++{ ++ cec_dev->cec_suspend = 0; ++ CEC_INFO("%s, suspend:%d\n", __func__, cec_dev->cec_suspend); ++ ++} ++#endif ++ ++static int aml_cec_probe(struct platform_device *pdev) ++{ ++ int i; ++ struct device *cdev; ++#ifdef CONFIG_OF ++ struct device_node *node = pdev->dev.of_node; ++ int irq_idx = 0, r; ++ const char *pin_name = NULL, *irq_name = NULL; ++ struct pinctrl *p; ++ struct vendor_info_data *vend; ++ struct resource *res; ++ resource_size_t *base; ++#endif ++ ++ cec_dev = kzalloc(sizeof(struct ao_cec_dev), GFP_KERNEL); ++ if (!cec_dev) { ++ CEC_ERR("alloc memory failed\n"); ++ return -ENOMEM; ++ } ++ cec_dev->dev_type = DEV_TYPE_TX; ++ cec_dev->dbg_dev = &pdev->dev; ++ cec_dev->tx_dev = get_hdmitx_device(); ++ phy_addr_test = 0; ++ ++ /* cdev registe */ ++ r = class_register(&aocec_class); ++ if (r) { ++ CEC_ERR("regist class failed\n"); ++ return -EINVAL; ++ } ++ pdev->dev.class = &aocec_class; ++ cec_dev->cec_info.dev_no = register_chrdev(0, CEC_DEV_NAME, ++ &hdmitx_cec_fops); ++ if (cec_dev->cec_info.dev_no < 0) { ++ CEC_ERR("alloc chrdev failed\n"); ++ return -EINVAL; ++ } ++ CEC_INFO("alloc chrdev %x\n", cec_dev->cec_info.dev_no); ++ cdev = device_create(&aocec_class, &pdev->dev, ++ MKDEV(cec_dev->cec_info.dev_no, 0), ++ NULL, CEC_DEV_NAME); ++ if (IS_ERR(cdev)) { ++ CEC_ERR("create chrdev failed, dev:%p\n", cdev); ++ unregister_chrdev(cec_dev->cec_info.dev_no, ++ CEC_DEV_NAME); ++ return -EINVAL; ++ } ++ ++ init_completion(&cec_dev->rx_ok); ++ init_completion(&cec_dev->tx_ok); ++ mutex_init(&cec_dev->cec_mutex); ++ spin_lock_init(&cec_dev->cec_reg_lock); ++ cec_dev->cec_thread = create_workqueue("cec_work"); ++ if (cec_dev->cec_thread == NULL) { ++ CEC_INFO("create work queue failed\n"); ++ return -EFAULT; ++ } ++ INIT_DELAYED_WORK(&cec_dev->cec_work, cec_task); ++ hrtimer_init(&cec_key_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ cec_key_timer.function = cec_key_up; ++ cec_dev->cec_info.remote_cec_dev = input_allocate_device(); ++ if (!cec_dev->cec_info.remote_cec_dev) ++ CEC_INFO("No enough memory\n"); ++ ++ cec_dev->cec_info.remote_cec_dev->name = "cec_input"; ++ ++ cec_dev->cec_info.remote_cec_dev->evbit[0] = BIT_MASK(EV_KEY); ++ cec_dev->cec_info.remote_cec_dev->keybit[BIT_WORD(BTN_0)] = ++ BIT_MASK(BTN_0); ++ cec_dev->cec_info.remote_cec_dev->id.bustype = BUS_ISA; ++ cec_dev->cec_info.remote_cec_dev->id.vendor = 0x1b8e; ++ cec_dev->cec_info.remote_cec_dev->id.product = 0x0cec; ++ cec_dev->cec_info.remote_cec_dev->id.version = 0x0001; ++ ++ for (i = 0; i < 160; i++) ++ set_bit(cec_key_map[i], cec_dev->cec_info.remote_cec_dev->keybit); ++ if (input_register_device(cec_dev->cec_info.remote_cec_dev)) { ++ CEC_INFO("Failed to register device\n"); ++ input_free_device(cec_dev->cec_info.remote_cec_dev); ++ } ++ ++#ifdef CONFIG_OF ++ /* pinmux set */ ++ if (of_get_property(node, "pinctrl-names", NULL)) { ++ r = of_property_read_string(node, ++ "pinctrl-names", ++ &pin_name); ++ if (!r) ++ p = devm_pinctrl_get_select(&pdev->dev, pin_name); ++ } ++ ++ /* irq set */ ++ irq_idx = of_irq_get(node, 0); ++ cec_dev->irq_cec = irq_idx; ++ if (of_get_property(node, "interrupt-names", NULL)) { ++ r = of_property_read_string(node, "interrupt-names", &irq_name); ++ if (!r) { ++ r = request_irq(irq_idx, &cec_isr_handler, IRQF_SHARED, ++ irq_name, (void *)cec_dev); ++ } ++ } ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) { ++ base = ioremap(res->start, res->end - res->start); ++ cec_dev->exit_reg = (void *)base; ++ } else { ++ CEC_INFO("no memory resource\n"); ++ cec_dev->exit_reg = NULL; ++ } ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (res) { ++ base = ioremap(res->start, res->end - res->start); ++ cec_dev->cec_reg = (void *)base; ++ } else { ++ CEC_ERR("no CEC reg resource\n"); ++ cec_dev->cec_reg = NULL; ++ } ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ if (res) { ++ base = ioremap(res->start, res->end - res->start); ++ cec_dev->hdmi_rxreg = (void *)base; ++ } else { ++ CEC_ERR("no hdmirx reg resource\n"); ++ cec_dev->hdmi_rxreg = NULL; ++ } ++ ++ r = of_property_read_u32(node, "port_num", &(cec_dev->port_num)); ++ if (r) { ++ CEC_ERR("not find 'port_num'\n"); ++ cec_dev->port_num = 1; ++ } ++ r = of_property_read_u32(node, "arc_port_mask", &(cec_dev->arc_port)); ++ if (r) { ++ CEC_ERR("not find 'arc_port_mask'\n"); ++ cec_dev->arc_port = 0; ++ } ++ ++ vend = &cec_dev->v_data; ++ r = of_property_read_string(node, "vendor_name", ++ (const char **)&(vend->vendor_name)); ++ if (r) ++ CEC_INFO("not find vendor name\n"); ++ ++ r = of_property_read_u32(node, "vendor_id", &(vend->vendor_id)); ++ if (r) ++ CEC_INFO("not find vendor id\n"); ++ ++ r = of_property_read_string(node, "product_desc", ++ (const char **)&(vend->product_desc)); ++ if (r) ++ CEC_INFO("not find product desc\n"); ++ ++ r = of_property_read_string(node, "cec_osd_string", ++ (const char **)&(vend->cec_osd_string)); ++ if (r) { ++ CEC_INFO("not find cec osd string\n"); ++ strcpy(vend->cec_osd_string, "AML TV/BOX"); ++ } ++ ++ /* get port sequence */ ++ node = of_find_node_by_path("/hdmirx"); ++ if (node == NULL) { ++ CEC_ERR("can't find hdmirx\n"); ++ cec_dev->port_seq = 0; ++ } else { ++ r = of_property_read_u32(node, "rx_port_maps", ++ &(cec_dev->port_seq)); ++ if (r) ++ CEC_INFO("not find rx_port_maps\n"); ++ } ++#endif ++#ifdef CONFIG_HAS_EARLYSUSPEND ++ aocec_suspend_handler.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 20; ++ aocec_suspend_handler.suspend = aocec_early_suspend; ++ aocec_suspend_handler.resume = aocec_late_resume; ++ aocec_suspend_handler.param = cec_dev; ++ register_early_suspend(&aocec_suspend_handler); ++#endif ++ /* for init */ ++ cec_pre_init(); ++ CEC_INFO("CEC_LOGICAL_ADDR0: 0x%x\n",aocec_rd_reg(CEC_LOGICAL_ADDR0)); ++ CEC_INFO("CEC_LOGICAL_ADDR1: 0x%x\n",aocec_rd_reg(CEC_LOGICAL_ADDR1)); ++ CEC_INFO("CEC_LOGICAL_ADDR2: 0x%x\n",aocec_rd_reg(CEC_LOGICAL_ADDR2)); ++ CEC_INFO("CEC_LOGICAL_ADDR3: 0x%x\n",aocec_rd_reg(CEC_LOGICAL_ADDR3)); ++ CEC_INFO("CEC_LOGICAL_ADDR4: 0x%x\n",aocec_rd_reg(CEC_LOGICAL_ADDR4)); ++ CEC_INFO("CEC log_addr: %d\n", cec_dev->cec_info.log_addr[0]) ++ queue_delayed_work(cec_dev->cec_thread, &cec_dev->cec_work, 0); ++ cec_dev->tx_dev->cec_init_ready = 1; ++ return 0; ++} ++ ++static int aml_cec_remove(struct platform_device *pdev) ++{ ++ CEC_INFO("cec uninit!\n"); ++ free_irq(cec_dev->irq_cec, (void *)cec_dev); ++ cec_dev->tx_dev->cec_init_ready = 0; ++ ++ if (cec_dev->cec_thread) { ++ cancel_delayed_work_sync(&cec_dev->cec_work); ++ destroy_workqueue(cec_dev->cec_thread); ++ } ++ input_unregister_device(cec_dev->cec_info.remote_cec_dev); ++ unregister_chrdev(cec_dev->cec_info.dev_no, CEC_DEV_NAME); ++ class_unregister(&aocec_class); ++ kfree(cec_dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int aml_cec_pm_prepare(struct device *dev) ++{ ++ cec_dev->cec_suspend = CEC_DEEP_SUSPEND; ++ CEC_INFO("%s, cec_suspend:%d\n", __func__, cec_dev->cec_suspend); ++ return 0; ++} ++ ++static void aml_cec_pm_complete(struct device *dev) ++{ ++ int exit = 0; ++ ++ if (cec_dev->exit_reg) { ++ exit = readl(cec_dev->exit_reg); ++ CEC_INFO("wake up flag:%x\n", exit); ++ } ++ if (((exit >> 28) & 0xf) == CEC_WAKEUP) { ++ cec_key_report(0); ++ } ++} ++ ++static int aml_cec_resume_noirq(struct device *dev) ++{ ++ CEC_INFO("cec resume noirq!\n"); ++ cec_dev->cec_info.power_status = TRANS_STANDBY_TO_ON; ++ return 0; ++} ++ ++static const struct dev_pm_ops aml_cec_pm = { ++ .prepare = aml_cec_pm_prepare, ++ .complete = aml_cec_pm_complete, ++ .resume_noirq = aml_cec_resume_noirq, ++}; ++#endif ++ ++#ifdef CONFIG_OF ++static const struct of_device_id aml_cec_dt_match[] = { ++ { ++ .compatible = "amlogic, amlogic-aocec", ++ }, ++ {}, ++}; ++#endif ++ ++static struct platform_driver aml_cec_driver = { ++ .driver = { ++ .name = "cectx", ++ .owner = THIS_MODULE, ++ #ifdef CONFIG_PM ++ .pm = &aml_cec_pm, ++ #endif ++ #ifdef CONFIG_OF ++ .of_match_table = aml_cec_dt_match, ++ #endif ++ }, ++ .probe = aml_cec_probe, ++ .remove = aml_cec_remove, ++}; ++ ++static int __init cec_init(void) ++{ ++ int ret; ++ ret = platform_driver_register(&aml_cec_driver); ++ return ret; ++} ++ ++static void __exit cec_uninit(void) ++{ ++ platform_driver_unregister(&aml_cec_driver); ++} ++ ++ ++module_init(cec_init); ++module_exit(cec_uninit); ++MODULE_DESCRIPTION("AMLOGIC HDMI TX CEC driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/amlogic/cec/hdmi_ao_cec.h b/drivers/amlogic/cec/hdmi_ao_cec.h +new file mode 100644 +index 0000000..965c2f9 +--- /dev/null ++++ b/drivers/amlogic/cec/hdmi_ao_cec.h +@@ -0,0 +1,179 @@ ++#ifndef __AO_CEC_H__ ++#define __AO_CEC_H__ ++ ++#define AO_BASE 0xc8100000 ++ ++#define AO_CEC_GEN_CNTL ((0x40 << 2)) ++#define AO_CEC_RW_REG ((0x41 << 2)) ++#define AO_CEC_INTR_MASKN ((0x42 << 2)) ++#define AO_CEC_INTR_CLR ((0x43 << 2)) ++#define AO_CEC_INTR_STAT ((0x44 << 2)) ++ ++#define AO_CRT_CLK_CNTL1 ((0x1a << 2)) ++ ++#define AO_DEBUG_REG0 ((0x28 << 2)) ++#define AO_DEBUG_REG1 ((0x29 << 2)) ++#define AO_DEBUG_REG2 ((0x2a << 2)) ++#define AO_DEBUG_REG3 ((0x2b << 2)) ++ ++/* read/write */ ++#define CEC_TX_MSG_0_HEADER 0x00 ++#define CEC_TX_MSG_1_OPCODE 0x01 ++#define CEC_TX_MSG_2_OP1 0x02 ++#define CEC_TX_MSG_3_OP2 0x03 ++#define CEC_TX_MSG_4_OP3 0x04 ++#define CEC_TX_MSG_5_OP4 0x05 ++#define CEC_TX_MSG_6_OP5 0x06 ++#define CEC_TX_MSG_7_OP6 0x07 ++#define CEC_TX_MSG_8_OP7 0x08 ++#define CEC_TX_MSG_9_OP8 0x09 ++#define CEC_TX_MSG_A_OP9 0x0A ++#define CEC_TX_MSG_B_OP10 0x0B ++#define CEC_TX_MSG_C_OP11 0x0C ++#define CEC_TX_MSG_D_OP12 0x0D ++#define CEC_TX_MSG_E_OP13 0x0E ++#define CEC_TX_MSG_F_OP14 0x0F ++ ++/* read/write */ ++#define CEC_TX_MSG_LENGTH 0x10 ++#define CEC_TX_MSG_CMD 0x11 ++#define CEC_TX_WRITE_BUF 0x12 ++#define CEC_TX_CLEAR_BUF 0x13 ++#define CEC_RX_MSG_CMD 0x14 ++#define CEC_RX_CLEAR_BUF 0x15 ++#define CEC_LOGICAL_ADDR0 0x16 ++#define CEC_LOGICAL_ADDR1 0x17 ++#define CEC_LOGICAL_ADDR2 0x18 ++#define CEC_LOGICAL_ADDR3 0x19 ++#define CEC_LOGICAL_ADDR4 0x1A ++#define CEC_CLOCK_DIV_H 0x1B ++#define CEC_CLOCK_DIV_L 0x1C ++ ++/* The following registers are for fine tuning CEC bit timing parameters. ++ * They are only valid in AO CEC, NOT valid in HDMITX CEC. ++ * The AO CEC's timing parameters are already set default to work with ++ * 32768Hz clock, so hopefully SW never need to program these registers. ++ * The timing registers are made programmable just in case. */ ++#define AO_CEC_QUIESCENT_25MS_BIT7_0 0x20 ++#define AO_CEC_QUIESCENT_25MS_BIT11_8 0x21 ++#define AO_CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22 ++#define AO_CEC_STARTBITMINL2H_3MS5_BIT8 0x23 ++#define AO_CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24 ++#define AO_CEC_STARTBITMAXL2H_3MS9_BIT8 0x25 ++#define AO_CEC_STARTBITMINH_0MS6_BIT7_0 0x26 ++#define AO_CEC_STARTBITMINH_0MS6_BIT8 0x27 ++#define AO_CEC_STARTBITMAXH_1MS0_BIT7_0 0x28 ++#define AO_CEC_STARTBITMAXH_1MS0_BIT8 0x29 ++#define AO_CEC_STARTBITMINTOTAL_4MS3_BIT7_0 0x2A ++#define AO_CEC_STARTBITMINTOTAL_4MS3_BIT9_8 0x2B ++#define AO_CEC_STARTBITMAXTOTAL_4MS7_BIT7_0 0x2C ++#define AO_CEC_STARTBITMAXTOTAL_4MS7_BIT9_8 0x2D ++#define AO_CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E ++#define AO_CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F ++#define AO_CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30 ++#define AO_CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31 ++#define AO_CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32 ++#define AO_CEC_LOGIC0MINL2H_1MS3_BIT8 0x33 ++#define AO_CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34 ++#define AO_CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35 ++#define AO_CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36 ++#define AO_CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37 ++#define AO_CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38 ++#define AO_CEC_LOGICMAXHIGH_2MS8_BIT8 0x39 ++#define AO_CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A ++#define AO_CEC_LOGICERRLOW_3MS4_BIT8 0x3B ++#define AO_CEC_NOMSMPPOINT_1MS05 0x3C ++#define AO_CEC_DELCNTR_LOGICERR 0x3E ++#define AO_CEC_TXTIME_17MS_BIT7_0 0x40 ++#define AO_CEC_TXTIME_17MS_BIT10_8 0x41 ++#define AO_CEC_TXTIME_2BIT_BIT7_0 0x42 ++#define AO_CEC_TXTIME_2BIT_BIT10_8 0x43 ++#define AO_CEC_TXTIME_4BIT_BIT7_0 0x44 ++#define AO_CEC_TXTIME_4BIT_BIT10_8 0x45 ++#define AO_CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46 ++#define AO_CEC_STARTBITNOML2H_3MS7_BIT8 0x47 ++#define AO_CEC_STARTBITNOMH_0MS8_BIT7_0 0x48 ++#define AO_CEC_STARTBITNOMH_0MS8_BIT8 0x49 ++#define AO_CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A ++#define AO_CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B ++#define AO_CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C ++#define AO_CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D ++#define AO_CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E ++#define AO_CEC_LOGIC1NOMH_1MS8_BIT8 0x4F ++#define AO_CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50 ++#define AO_CEC_LOGIC0NOMH_0MS9_BIT8 0x51 ++#define AO_CEC_LOGICERRLOW_3MS6_BIT7_0 0x52 ++#define AO_CEC_LOGICERRLOW_3MS6_BIT8 0x53 ++#define AO_CEC_CHKCONTENTION_0MS1 0x54 ++#define AO_CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56 ++#define AO_CEC_PREPARENXTBIT_0MS05_BIT8 0x57 ++#define AO_CEC_NOMSMPACKPOINT_0MS45 0x58 ++#define AO_CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A ++#define AO_CEC_ACK0NOML2H_1MS5_BIT8 0x5B ++ ++#define AO_CEC_BUGFIX_DISABLE_0 0x60 ++#define AO_CEC_BUGFIX_DISABLE_1 0x61 ++ ++/* read only */ ++#define CEC_RX_MSG_0_HEADER 0x80 ++#define CEC_RX_MSG_1_OPCODE 0x81 ++#define CEC_RX_MSG_2_OP1 0x82 ++#define CEC_RX_MSG_3_OP2 0x83 ++#define CEC_RX_MSG_4_OP3 0x84 ++#define CEC_RX_MSG_5_OP4 0x85 ++#define CEC_RX_MSG_6_OP5 0x86 ++#define CEC_RX_MSG_7_OP6 0x87 ++#define CEC_RX_MSG_8_OP7 0x88 ++#define CEC_RX_MSG_9_OP8 0x89 ++#define CEC_RX_MSG_A_OP9 0x8A ++#define CEC_RX_MSG_B_OP10 0x8B ++#define CEC_RX_MSG_C_OP11 0x8C ++#define CEC_RX_MSG_D_OP12 0x8D ++#define CEC_RX_MSG_E_OP13 0x8E ++#define CEC_RX_MSG_F_OP14 0x8F ++ ++/* read only */ ++#define CEC_RX_MSG_LENGTH 0x90 ++#define CEC_RX_MSG_STATUS 0x91 ++#define CEC_RX_NUM_MSG 0x92 ++#define CEC_TX_MSG_STATUS 0x93 ++#define CEC_TX_NUM_MSG 0x94 ++ ++/* tx_msg_cmd definition */ ++#define TX_NO_OP 0 /* No transaction */ ++#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */ ++#define TX_ABORT 2 /* Abort transmitting earliest message */ ++/* Overwrite earliest message in buffer and transmit next message */ ++#define TX_REQ_NEXT 3 ++ ++/* tx_msg_status definition */ ++#define TX_IDLE 0 /* No transaction */ ++#define TX_BUSY 1 /* Transmitter is busy */ ++/* Message has been successfully transmitted */ ++#define TX_DONE 2 ++#define TX_ERROR 3 /* Message has been transmitted with error */ ++ ++/* rx_msg_cmd */ ++#define RX_NO_OP 0 /* No transaction */ ++#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */ ++#define RX_DISABLE 2 /* Disable receiving latest message */ ++/* Clear earliest message from buffer and read next message */ ++#define RX_ACK_NEXT 3 ++ ++/* rx_msg_status */ ++#define RX_IDLE 0 /* No transaction */ ++#define RX_BUSY 1 /* Receiver is busy */ ++#define RX_DONE 2 /* Message has been received successfully */ ++#define RX_ERROR 3 /* Message has been received with error */ ++ ++#ifdef CONFIG_TVIN_HDMI ++extern unsigned long hdmirx_rd_top(unsigned long addr); ++extern void hdmirx_wr_top(unsigned long addr, unsigned long data); ++#define TOP_HPD_PWR5V 0x002 ++#define TOP_ARCTX_CNTL 0x010 ++#endif ++ ++unsigned int aocec_rd_reg(unsigned long addr); ++void aocec_wr_reg(unsigned long addr, unsigned long data); ++ ++#endif /* __AO_CEC_H__ */ +diff --git a/drivers/amlogic/hdmi/Kconfig b/drivers/amlogic/hdmi/Kconfig +index 28ec6ba..1edf20a 100644 +--- a/drivers/amlogic/hdmi/Kconfig ++++ b/drivers/amlogic/hdmi/Kconfig +@@ -35,11 +35,13 @@ config AML_HDMI_TX_HDCP + endif + + if AML_HDMI_TX_20 ++if !AML_AO_CEC + config AML_HDMI_TX_NEW_CEC_DRIVER + bool "HDMI new CEC driver" + default n + help + hdmitx uses new CEC driver + endif ++endif + + endmenu +diff --git a/drivers/amlogic/hdmi/hdmi_tx_20/Makefile b/drivers/amlogic/hdmi/hdmi_tx_20/Makefile +index 6ecaf55..64132b8 100644 +--- a/drivers/amlogic/hdmi/hdmi_tx_20/Makefile ++++ b/drivers/amlogic/hdmi/hdmi_tx_20/Makefile +@@ -3,11 +3,14 @@ obj-$(CONFIG_AML_HDMI_TX_20) += hdmitx20.o + hdmitx20-objs := hdmi_tx_main.o hdmi_tx_video.o hdmi_tx_audio.o hdmi_tx_edid.o \ + hdmi_tx_audio.o hdmi_tx_hdcp.o hdmi_tx_scdc.o + ++ifndef CONFIG_AML_AO_CEC + ifdef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER + hdmitx20-objs += amlogic_cec.o + else + hdmitx20-objs += hdmi_cec_key.o hdmi_tx_cec.o + endif ++endif ++ + obj-$(CONFIG_AML_HDMI_TX_20) += hw/ + + #EXTRA_CFLAGS += -O2 +diff --git a/drivers/amlogic/hdmi/hdmi_tx_20/hdmi_tx_main.c b/drivers/amlogic/hdmi/hdmi_tx_20/hdmi_tx_main.c +index 2b3f896..f05fe8f 100644 +--- a/drivers/amlogic/hdmi/hdmi_tx_20/hdmi_tx_main.c ++++ b/drivers/amlogic/hdmi/hdmi_tx_20/hdmi_tx_main.c +@@ -1551,8 +1551,9 @@ void hdmitx_hpd_plugin_handler(struct work_struct *work) + set_disp_mode_auto(); + hdmitx_set_audio(hdev, &(hdev->cur_audio_param), hdmi_ch); + switch_set_state(&sdev, 1); ++#ifndef CONFIG_AML_AO_CEC + cec_node_init(hdev); +- ++#endif + hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGIN; + mutex_unlock(&setclk_mutex); + } +diff --git a/drivers/amlogic/hdmi/hdmi_tx_20/hw/Makefile b/drivers/amlogic/hdmi/hdmi_tx_20/hw/Makefile +index d4cd9d4..fb22a69 100644 +--- a/drivers/amlogic/hdmi/hdmi_tx_20/hw/Makefile ++++ b/drivers/amlogic/hdmi/hdmi_tx_20/hw/Makefile +@@ -1 +1,4 @@ +-obj-y += hdmi_tx_hw.o reg_ops.o enc_cfg_hw.o hdmi_tx_cec_hw.o hdmi_tx_ddc.o ++obj-y += hdmi_tx_hw.o reg_ops.o enc_cfg_hw.o hdmi_tx_ddc.o ++ifndef CONFIG_AML_AO_CEC ++obj-y += hdmi_tx_cec_hw.o ++endif +diff --git a/drivers/amlogic/hdmi/hdmi_tx_20/hw/reg_ops.c b/drivers/amlogic/hdmi/hdmi_tx_20/hw/reg_ops.c +index 33ec391..4132cd4 100644 +--- a/drivers/amlogic/hdmi/hdmi_tx_20/hw/reg_ops.c ++++ b/drivers/amlogic/hdmi/hdmi_tx_20/hw/reg_ops.c +@@ -33,7 +33,9 @@ + #include "hdmi_tx_reg.h" + + static int dbg_en; ++#ifndef CONFIG_AML_AO_CEC + static DEFINE_SPINLOCK(reg_lock2); ++#endif + + /* + * RePacket HDMI related registers rd/wr +@@ -286,6 +288,7 @@ void hdmitx_rd_check_reg(unsigned int addr, unsigned int exp_data, + } \ + } while (0) + ++#ifndef CONFIG_AML_AO_CEC + unsigned long aocec_rd_reg(unsigned long addr) + { + unsigned long data32; +@@ -317,6 +320,7 @@ void aocec_wr_reg(unsigned long addr, unsigned long data) + hd_write_reg(P_AO_CEC_RW_REG, data32); + spin_unlock_irqrestore(®_lock2, flags); + } /* aocec_wr_only_reg */ ++#endif + + + MODULE_PARM_DESC(dbg_en, "\n debug_level\n"); +diff --git a/include/linux/amlogic/hdmi_tx/hdmi_tx_cec_20.h b/include/linux/amlogic/hdmi_tx/hdmi_tx_cec_20.h +new file mode 100644 +index 0000000..4fdb10e +--- /dev/null ++++ b/include/linux/amlogic/hdmi_tx/hdmi_tx_cec_20.h +@@ -0,0 +1,356 @@ ++/* ++ * include/linux/amlogic/hdmi_tx/hdmi_tx_cec.h ++ * ++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++*/ ++ ++ ++#ifndef _TX_CEC_H_ ++#define _TX_CEC_H_ ++ ++#include ++#include ++#include ++#include ++ ++#define CEC0_LOG_ADDR 4 /* MBX logical address */ ++#define TV_CEC_INTERVAL (HZ*3) ++ ++#define CEC_VERSION "v1.3" ++#define _RX_DATA_BUF_SIZE_ 16 ++ ++#define AO_CEC /* for switch between aocec and hdmi cec2.0 */ ++ ++#define MAX_MSG 16 ++#define MAX_NUM_OF_DEV 16 ++ ++enum _cec_log_dev_addr_e { ++ CEC_TV_ADDR = 0x00, ++ CEC_RECORDING_DEVICE_1_ADDR, ++ CEC_RECORDING_DEVICE_2_ADDR, ++ CEC_TUNER_1_ADDR, ++ CEC_PLAYBACK_DEVICE_1_ADDR, ++ CEC_AUDIO_SYSTEM_ADDR, ++ CEC_TUNER_2_ADDR, ++ CEC_TUNER_3_ADDR, ++ CEC_PLAYBACK_DEVICE_2_ADDR, ++ CEC_RECORDING_DEVICE_3_ADDR, ++ CEC_TUNER_4_ADDR, ++ CEC_PLAYBACK_DEVICE_3_ADDR, ++ CEC_RESERVED_1_ADDR, ++ CEC_RESERVED_2_ADDR, ++ CEC_FREE_USE_ADDR, ++ CEC_UNREGISTERED_ADDR ++}; ++ ++#define CEC_BROADCAST_ADDR CEC_UNREGISTERED_ADDR ++ ++#define CEC_TV (1 << CEC_TV_ADDR) ++#define CEC_RECORDING_DEVICE_1 (1 << CEC_RECORDING_DEVICE_1_ADDR) ++#define CEC_RECORDING_DEVICE_2 (1 << CEC_RECORDING_DEVICE_2_ADDR) ++#define CEC_TUNER_1 (1 << CEC_TUNER_1_ADDR) ++#define CEC_PLAYBACK_DEVICE_1 (1 << CEC_PLAYBACK_DEVICE_1_ADDR) ++#define CEC_AUDIO_SYSTEM (1 << CEC_AUDIO_SYSTEM_ADDR) ++#define CEC_TUNER_2 (1 << CEC_TUNER_2_ADDR) ++#define CEC_TUNER_3 (1 << CEC_TUNER_3_ADDR) ++#define CEC_PLAYBACK_DEVICE_2 (1 << CEC_PLAYBACK_DEVICE_2_ADDR) ++#define CEC_RECORDING_DEVICE_3 (1 << CEC_RECORDING_DEVICE_3_ADDR) ++#define CEC_TUNER_4 (1 << CEC_TUNER_4_ADDR) ++#define CEC_PLAYBACK_DEVICE_3 (1 << CEC_PLAYBACK_DEVICE_3_ADDR) ++#define CEC_RESERVED_1 (1 << CEC_RESERVED_1_ADDR) ++#define CEC_RESERVED_2 (1 << CEC_RESERVED_2_ADDR) ++#define CEC_FREE_USE (1 << CEC_FREE_USE_ADDR) ++#define CEC_UNREGISTERED (1 << CEC_UNREGISTERED_ADDR) ++ ++#define CEC_DISPLAY_DEVICE (CEC_TV | CEC_FREE_USE) ++#define CEC_RECORDING_DEVICE (CEC_RECORDING_DEVICE_1 \ ++ | CEC_RECORDING_DEVICE_2 | CEC_RECORDING_DEVICE_3) ++#define CEC_PLAYBACK_DEVICE (CEC_PLAYBACK_DEVICE_1 \ ++ | CEC_PLAYBACK_DEVICE_2 | CEC_PLAYBACK_DEVICE_3) ++#define CEC_TUNER_DEVICE (CEC_TUNER_1 | CEC_TUNER_2 \ ++ | CEC_TUNER_3 | CEC_TUNER_4) ++#define CEC_AUDIO_SYSTEM_DEVICE (CEC_AUDIO_SYSTEM) ++ ++#define CEC_IOC_MAGIC 'C' ++#define CEC_IOC_GET_PHYSICAL_ADDR _IOR(CEC_IOC_MAGIC, 0x00, uint16_t) ++#define CEC_IOC_GET_VERSION _IOR(CEC_IOC_MAGIC, 0x01, int) ++#define CEC_IOC_GET_VENDOR_ID _IOR(CEC_IOC_MAGIC, 0x02, uint32_t) ++#define CEC_IOC_GET_PORT_INFO _IOR(CEC_IOC_MAGIC, 0x03, int) ++#define CEC_IOC_GET_PORT_NUM _IOR(CEC_IOC_MAGIC, 0x04, int) ++#define CEC_IOC_GET_SEND_FAIL_REASON _IOR(CEC_IOC_MAGIC, 0x05, uint32_t) ++#define CEC_IOC_SET_OPTION_WAKEUP _IOW(CEC_IOC_MAGIC, 0x06, uint32_t) ++#define CEC_IOC_SET_OPTION_ENALBE_CEC _IOW(CEC_IOC_MAGIC, 0x07, uint32_t) ++#define CEC_IOC_SET_OPTION_SYS_CTRL _IOW(CEC_IOC_MAGIC, 0x08, uint32_t) ++#define CEC_IOC_SET_OPTION_SET_LANG _IOW(CEC_IOC_MAGIC, 0x09, uint32_t) ++#define CEC_IOC_GET_CONNECT_STATUS _IOR(CEC_IOC_MAGIC, 0x0A, uint32_t) ++#define CEC_IOC_ADD_LOGICAL_ADDR _IOW(CEC_IOC_MAGIC, 0x0B, uint32_t) ++#define CEC_IOC_CLR_LOGICAL_ADDR _IOW(CEC_IOC_MAGIC, 0x0C, uint32_t) ++#define CEC_IOC_SET_DEV_TYPE _IOW(CEC_IOC_MAGIC, 0x0D, uint32_t) ++#define CEC_IOC_SET_ARC_ENABLE _IOW(CEC_IOC_MAGIC, 0x0E, uint32_t) ++ ++#define CEC_FAIL_NONE 0 ++#define CEC_FAIL_NACK 1 ++#define CEC_FAIL_BUSY 2 ++#define CEC_FAIL_OTHER 3 ++ ++enum hdmi_port_type { ++ HDMI_INPUT = 0, ++ HDMI_OUTPUT = 1 ++}; ++ ++struct hdmi_port_info { ++ int type; ++ /* Port ID should start from 1 which corresponds to HDMI "port 1". */ ++ int port_id; ++ int cec_supported; ++ int arc_supported; ++ uint16_t physical_address; ++}; ++ ++enum cec_dev_type_addr { ++ CEC_DISPLAY_DEVICE_TYPE = 0x0, ++ CEC_RECORDING_DEVICE_TYPE, ++ CEC_RESERVED_DEVICE_TYPE, ++ CEC_TUNER_DEVICE_TYPE, ++ CEC_PLAYBACK_DEVICE_TYPE, ++ CEC_AUDIO_SYSTEM_DEVICE_TYPE, ++ CEC_UNREGISTERED_DEVICE_TYPE, ++}; ++ ++enum cec_feature_abort_e { ++ CEC_UNRECONIZED_OPCODE = 0x0, ++ CEC_NOT_CORRECT_MODE_TO_RESPOND, ++ CEC_CANNOT_PROVIDE_SOURCE, ++ CEC_INVALID_OPERAND, ++ CEC_REFUSED, ++ CEC_UNABLE_TO_DETERMINE, ++}; ++ ++/* ++ * CEC OPCODES ++ */ ++#define CEC_OC_ABORT_MESSAGE 0xFF ++#define CEC_OC_ACTIVE_SOURCE 0x82 ++#define CEC_OC_CEC_VERSION 0x9E ++#define CEC_OC_CLEAR_ANALOGUE_TIMER 0x33 ++#define CEC_OC_CLEAR_DIGITAL_TIMER 0x99 ++#define CEC_OC_CLEAR_EXTERNAL_TIMER 0xA1 ++#define CEC_OC_DECK_CONTROL 0x42 ++#define CEC_OC_DECK_STATUS 0x1B ++#define CEC_OC_DEVICE_VENDOR_ID 0x87 ++#define CEC_OC_FEATURE_ABORT 0x00 ++#define CEC_OC_GET_CEC_VERSION 0x9F ++#define CEC_OC_GET_MENU_LANGUAGE 0x91 ++#define CEC_OC_GIVE_AUDIO_STATUS 0x71 ++#define CEC_OC_GIVE_DECK_STATUS 0x1A ++#define CEC_OC_GIVE_DEVICE_POWER_STATUS 0x8F ++#define CEC_OC_GIVE_DEVICE_VENDOR_ID 0x8C ++#define CEC_OC_GIVE_OSD_NAME 0x46 ++#define CEC_OC_GIVE_PHYSICAL_ADDRESS 0x83 ++#define CEC_OC_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7D ++#define CEC_OC_GIVE_TUNER_DEVICE_STATUS 0x08 ++#define CEC_OC_IMAGE_VIEW_ON 0x04 ++#define CEC_OC_INACTIVE_SOURCE 0x9D ++#define CEC_OC_MENU_REQUEST 0x8D ++#define CEC_OC_MENU_STATUS 0x8E ++#define CEC_OC_PLAY 0x41 ++#define CEC_OC_POLLING_MESSAGE 0xFC ++#define CEC_OC_RECORD_OFF 0x0B ++#define CEC_OC_RECORD_ON 0x09 ++#define CEC_OC_RECORD_STATUS 0x0A ++#define CEC_OC_RECORD_TV_SCREEN 0x0F ++#define CEC_OC_REPORT_AUDIO_STATUS 0x7A ++#define CEC_OC_REPORT_PHYSICAL_ADDRESS 0x84 ++#define CEC_OC_REPORT_POWER_STATUS 0x90 ++#define CEC_OC_REQUEST_ACTIVE_SOURCE 0x85 ++#define CEC_OC_ROUTING_CHANGE 0x80 ++#define CEC_OC_ROUTING_INFORMATION 0x81 ++#define CEC_OC_SELECT_ANALOGUE_SERVICE 0x92 ++#define CEC_OC_SELECT_DIGITAL_SERVICE 0x93 ++#define CEC_OC_SET_ANALOGUE_TIMER 0x34 ++#define CEC_OC_SET_AUDIO_RATE 0x9A ++#define CEC_OC_SET_DIGITAL_TIMER 0x97 ++#define CEC_OC_SET_EXTERNAL_TIMER 0xA2 ++#define CEC_OC_SET_MENU_LANGUAGE 0x32 ++#define CEC_OC_SET_OSD_NAME 0x47 ++#define CEC_OC_SET_OSD_STRING 0x64 ++#define CEC_OC_SET_STREAM_PATH 0x86 ++#define CEC_OC_SET_SYSTEM_AUDIO_MODE 0x72 ++#define CEC_OC_SET_TIMER_PROGRAM_TITLE 0x67 ++#define CEC_OC_STANDBY 0x36 ++#define CEC_OC_SYSTEM_AUDIO_MODE_REQUEST 0x70 ++#define CEC_OC_SYSTEM_AUDIO_MODE_STATUS 0x7E ++#define CEC_OC_TEXT_VIEW_ON 0x0D ++#define CEC_OC_TIMER_CLEARED_STATUS 0x43 ++#define CEC_OC_TIMER_STATUS 0x35 ++#define CEC_OC_TUNER_DEVICE_STATUS 0x07 ++#define CEC_OC_TUNER_STEP_DECREMENT 0x06 ++#define CEC_OC_TUNER_STEP_INCREMENT 0x05 ++#define CEC_OC_USER_CONTROL_PRESSED 0x44 ++#define CEC_OC_USER_CONTROL_RELEASED 0x45 ++#define CEC_OC_VENDOR_COMMAND 0x89 ++#define CEC_OC_VENDOR_COMMAND_WITH_ID 0xA0 ++#define CEC_OC_VENDOR_REMOTE_BUTTON_DOWN 0x8A ++#define CEC_OC_VENDOR_REMOTE_BUTTON_UP 0x8B ++ ++/* cec global struct */ ++ ++enum cec_node_status_e { ++ STATE_UNKNOWN = 0x00, ++ STATE_START, ++ STATE_STOP ++}; ++ ++enum cec_power_status_e { ++ POWER_ON = 0x00, ++ POWER_STANDBY, ++ TRANS_STANDBY_TO_ON, ++ TRANS_ON_TO_STANDBY, ++}; ++ ++enum status_req_mode_e { ++ STATUS_REQ_ON = 1, ++ STATUS_REQ_OFF, ++ STATUS_REQ_ONCE, ++}; ++ ++enum deck_info_e { ++ DECK_UNKNOWN_STATUS = 0, ++ DECK_PLAY = 0X11, ++ DECK_RECORD, ++ DECK_PLAY_REVERSE, ++ DECK_STILL, ++ DECK_SLOW, ++ DECK_SLOW_REVERSE, ++ DECK_FAST_FORWARD, ++ DECK_FAST_REVERSE, ++ DECK_NO_MEDIA, ++ DECK_STOP, ++ DECK_SKIP_FORWARD_WIND, ++ DECK_SKIP_REVERSE_REWIND, ++ DECK_INDEX_SEARCH_FORWARD, ++ DECK_INDEX_SEARCH_REVERSE, ++ DECK_OTHER_STATUS, ++}; ++ ++enum deck_cnt_mode_e { ++ DECK_CNT_SKIP_FORWARD_WIND = 1, ++ DECK_CNT_SKIP_REVERSE_REWIND, ++ DECK_CNT_STOP, ++ DECK_CNT_EJECT, ++}; ++ ++enum play_mode_e { ++ PLAY_FORWARD = 0X24, ++ PLAY_REVERSE = 0X20, ++ PLAY_STILL = 0X25, ++ FAST_FORWARD_MIN_SPEED = 0X05, ++ FAST_FORWARD_MEDIUM_SPEED = 0X06, ++ FAST_FORWARD_MAX_SPEED = 0X07, ++ FAST_REVERSE_MIN_SPEED = 0X09, ++ FAST_REVERSE_MEDIUM_SPEED = 0X0A, ++ FAST_REVERSE_MAX_SPEED = 0X0B, ++ SLOW_FORWARD_MIN_SPEED = 0X15, ++ SLOW_FORWARD_MEDIUM_SPEED = 0X16, ++ SLOW_FORWARD_MAX_SPEED = 0X17, ++ SLOW_REVERSE_MIN_SPEED = 0X19, ++ SLOW_REVERSE_MEDIUM_SPEED = 0X1A, ++ SLOW_REVERSE_MAX_SPEED = 0X1B, ++}; ++ ++enum cec_version_e { ++ CEC_VERSION_11 = 0, ++ CEC_VERSION_12, ++ CEC_VERSION_12A, ++ CEC_VERSION_13, ++ CEC_VERSION_13A, ++ CEC_VERSION_14A, ++}; ++ ++#define INFO_MASK_CEC_VERSION (1<<0) ++#define INFO_MASK_VENDOR_ID (1<<1) ++#define INFO_MASK_DEVICE_TYPE (1<<2) ++#define INFO_MASK_POWER_STATUS (1<<3) ++#define INFO_MASK_PHYSICAL_ADDRESS (1<<4) ++#define INFO_MASK_LOGIC_ADDRESS (1<<5) ++#define INFO_MASK_OSD_NAME (1<<6) ++#define INFO_MASK_MENU_STATE (1<<7) ++#define INFO_MASK_MENU_LANGUAGE (1<<8) ++#define INFO_MASK_DECK_INfO (1<<9) ++#define INFO_MASK_PLAY_MODE (1<<10) ++ ++/*CEC UI MASK*/ ++#define CEC_FUNC_MSAK 0 ++#define ONE_TOUCH_PLAY_MASK 1 ++#define ONE_TOUCH_STANDBY_MASK 2 ++#define AUTO_POWER_ON_MASK 3 ++ ++ ++struct cec_node_info_t { ++}; ++ ++/* ++ * only for 1 tx device ++ */ ++struct cec_global_info_t { ++ dev_t dev_no; ++ unsigned int open_count; ++ unsigned int hal_ctl; /* message controled by hal */ ++ unsigned int vendor_id:24; ++ unsigned int power_status; ++ unsigned int menu_lang; ++ unsigned int cec_version; ++ unsigned int log_addr[5]; ++ unsigned int menu_status; ++ unsigned char osd_name[16]; ++ struct input_dev *remote_cec_dev; /* cec input device */ ++ struct hdmitx_dev *hdmitx_device; ++}; ++ ++enum cec_device_menu_state_e { ++ DEVICE_MENU_ACTIVE = 0, ++ DEVICE_MENU_INACTIVE, ++}; ++ ++int cec_ll_tx(const unsigned char *msg, unsigned char len); ++int cec_ll_rx(unsigned char *msg, unsigned char *len); ++int cec_rx_buf_check(void); ++ ++void cec_pinmux_set(void); ++void cec_arbit_bit_time_set(unsigned , unsigned , unsigned); ++void cec_clear_buf(unsigned int flag); ++void cec_keep_reset(void); ++void cec_logicaddr_set(int logicaddr, int logreg); ++void cec_logicaddr_clear(void); ++void cec_logicaddr_setByMask(unsigned int mask); ++void ao_cec_init(void); ++void tx_irq_handle(void); ++ ++unsigned int cec_config(unsigned int value, bool wr_flag); ++unsigned int cec_intr_stat(void); ++unsigned int cec_phyaddr_config(unsigned int value, bool wr_flag); ++unsigned int cec_logicaddr_config(unsigned int value, bool wr_flag); ++ ++void cec_user_control_pressed_irq(unsigned char message_irq); ++void cec_user_control_released_irq(void); ++extern __u16 cec_key_map[160]; ++void cec_send_simplink_alive(void); ++void cec_send_simplink_ack(void); ++ ++extern bool cec_msg_dbg_en; ++extern struct cec_global_info_t cec_info; ++extern void cec_rx_buf_clear(void); ++extern int get_cec_tx_fail(void); ++#endif ++ +-- +2.1.4 +