From c767c008c599058500c14abc2786fcb53a9a9acd Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Fri, 4 Mar 2016 11:22:22 +0100 Subject: [PATCH 01/21] new amlogic cec driver redesigned initialization - code of delayed init now part of open call - interrupt handling moved into open/release - removed some debug lines - repaired logical address detection --- .../hdmi_tx_hw_20/hdmi_tx_cec_hw.c | 3 +- drivers/amlogic/hdmi/Kconfig | 8 + drivers/amlogic/hdmi/hdmi_tx/Makefile | 8 +- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 671 ++++++++++++++++++ drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c | 30 +- .../amlogic/input/new_remote/remote_func.c | 6 + drivers/amlogic/input/remote/am_remote.c | 2 + 7 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c diff --git a/arch/arm/mach-mesong9bb/hdmi_tx_hw_20/hdmi_tx_cec_hw.c b/arch/arm/mach-mesong9bb/hdmi_tx_hw_20/hdmi_tx_cec_hw.c index a2a78bd22762..762feba41335 100644 --- a/arch/arm/mach-mesong9bb/hdmi_tx_hw_20/hdmi_tx_cec_hw.c +++ b/arch/arm/mach-mesong9bb/hdmi_tx_hw_20/hdmi_tx_cec_hw.c @@ -996,6 +996,7 @@ void cec_tx_irq_handle(void) #endif } +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER void cec_polling_online_dev(int log_addr, int *bool) { #ifdef AO_CEC @@ -1005,7 +1006,7 @@ void cec_polling_online_dev(int log_addr, int *bool) #endif hdmi_print(INF, CEC "CEC: poll online logic device: 0x%x BOOL: %d\n", log_addr, *bool); } - +#endif // DELETE LATER, TEST ONLY void cec_test_(unsigned int cmd) diff --git a/drivers/amlogic/hdmi/Kconfig b/drivers/amlogic/hdmi/Kconfig index 71f6bf848ff6..1bfc289901a7 100755 --- a/drivers/amlogic/hdmi/Kconfig +++ b/drivers/amlogic/hdmi/Kconfig @@ -24,4 +24,12 @@ config AML_HDMI_TX_HDCP hdmitx hdcp enable, it should be enalbe if board burned hdmitx hdcp keys endif +if AML_HDMI_TX +config AML_HDMI_TX_NEW_CEC_DRIVER + bool "HDMI new CEC driver" + default n + help + hdmitx uses new CEC driver +endif + endmenu diff --git a/drivers/amlogic/hdmi/hdmi_tx/Makefile b/drivers/amlogic/hdmi/hdmi_tx/Makefile index 7a944cd7f62e..f74ec1f18d1c 100755 --- a/drivers/amlogic/hdmi/hdmi_tx/Makefile +++ b/drivers/amlogic/hdmi/hdmi_tx/Makefile @@ -1,5 +1,11 @@ obj-$(CONFIG_AML_HDMI_TX) += hdmitx.o -hdmitx-objs := hdmi_tx.o hdmi_tx_cec.o hdmi_cec_key.o hdmi_tx_video.o hdmi_tx_audio.o hdmi_tx_edid.o hdmi_tx_audio.o hdmi_tx_hdcp.o hdmi_tx_compliance.o +hdmitx-objs := hdmi_tx.o hdmi_tx_video.o hdmi_tx_audio.o hdmi_tx_edid.o hdmi_tx_audio.o hdmi_tx_hdcp.o hdmi_tx_compliance.o + +ifdef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER + hdmitx-objs += amlogic_cec.o +else + hdmitx-objs += hdmi_tx_cec.o hdmi_cec_key.o +endif #EXTRA_CFLAGS += -O2 diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c new file mode 100644 index 000000000000..a3556b31ec1b --- /dev/null +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -0,0 +1,671 @@ +/* linux/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c + * + * Copyright (c) 2016 Gerald Dachs + * + * CEC interface file for Amlogic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#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 + +#define CONFIG_TV_DEBUG // for verbose output +//#undef CONFIG_TV_DEBUG +unsigned long amlogic_cec_debug_flag = 1; + +MODULE_AUTHOR("Gerald Dachs"); +MODULE_DESCRIPTION("Amlogic CEC driver"); +MODULE_LICENSE("GPL"); + +//unused, only left to satisfy the linker +bool cec_msg_dbg_en = 1; + +#define DRV_NAME "amlogic_cec" +#ifndef amlogic_cec_log_dbg +#define amlogic_cec_log_dbg(fmt, ...) \ + if (amlogic_cec_debug_flag) \ + printk(KERN_INFO "[%s] %s(): " fmt, DRV_NAME, __func__, ##__VA_ARGS__) +#endif + +#define CEC_IOC_MAGIC 'c' +#define CEC_IOC_SETLADDR _IOW(CEC_IOC_MAGIC, 0, unsigned int) +#define CEC_IOC_GETPADDR _IO(CEC_IOC_MAGIC, 1) + +#define VERSION "0.0.1" /* Driver version number */ +#define CEC_MINOR 243 /* Major 10, Minor 242, /dev/cec */ + +/* CEC Rx buffer size */ +#define CEC_RX_BUFF_SIZE 16 +/* CEC Tx buffer size */ +#define CEC_TX_BUFF_SIZE 16 + +static DEFINE_SEMAPHORE(init_mutex); + +struct cec_rx_list { + u8 buffer[CEC_RX_BUFF_SIZE]; + unsigned char size; + struct list_head list; +}; + +struct cec_rx_struct { + spinlock_t lock; + wait_queue_head_t waitq; + atomic_t state; + struct list_head list; +}; + +struct cec_tx_struct { + spinlock_t lock; + wait_queue_head_t waitq; + atomic_t state; +}; + +enum cec_state { + STATE_RX, + STATE_TX, + STATE_DONE, + STATE_ERROR +}; + +static char banner[] __initdata = + "Amlogic CEC Driver, (c) 2016 Gerald Dachs"; + +static struct cec_rx_struct cec_rx_struct; + +static struct cec_tx_struct cec_tx_struct; + +static atomic_t hdmi_on = ATOMIC_INIT(0); + +cec_global_info_t cec_global_info; + +static hdmitx_dev_t* hdmitx_device = NULL; + +static void amlogic_cec_set_rx_state(enum cec_state state) +{ + atomic_set(&cec_rx_struct.state, state); +} + +static void amlogic_cec_set_tx_state(enum cec_state state) +{ + atomic_set(&cec_tx_struct.state, state); +} + +static void amlogic_cec_msg_dump(char * msg_tag, const unsigned char *data, unsigned char count) +{ + int i; + int pos; + unsigned char msg_log_buf[128] = { 0 }; + + if (amlogic_cec_debug_flag == 1) + { + pos = 0; + pos += sprintf(msg_log_buf + pos, "msg %s len: %d dat: ", msg_tag, count); + for (i = 0; i < count; ++i) + { + pos += sprintf(msg_log_buf + pos, "%02x ", data[i]); + } + pos += sprintf(msg_log_buf + pos, "\n"); + msg_log_buf[pos] = '\0'; + hdmi_print(INF, "[amlogic_cec] dump: %s", msg_log_buf); + } +} + +static unsigned int amlogic_cec_read_reg(unsigned int reg) +{ +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + return hdmi_rd_reg(CEC0_BASE_ADDR + reg); +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + return aocec_rd_reg(reg); +#endif +} + +static void amlogic_cec_write_reg(unsigned int reg, unsigned int value) +{ +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + hdmi_wr_reg(CEC0_BASE_ADDR + reg, value); +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + aocec_wr_reg(reg, value); +#endif +} + +static int amlogic_cec_read_hw(unsigned char *data, unsigned char *count) +{ + int ret = -1; + int valid_msg; + int rx_msg_status; + int rx_num_msg; + + rx_msg_status = amlogic_cec_read_reg(CEC_RX_MSG_STATUS); + rx_num_msg = amlogic_cec_read_reg(CEC_RX_NUM_MSG); + + amlogic_cec_log_dbg("rx_msg_status: %d, rx_num_msg: %d\n", rx_msg_status, rx_num_msg); + + valid_msg = (RX_DONE == rx_msg_status) && (1 == rx_num_msg); + + if (valid_msg) + { + int i; + + *count = amlogic_cec_read_reg(CEC_RX_MSG_LENGTH) + 1; + for (i = 0; i < (*count) && i < CEC_RX_BUFF_SIZE; ++i) + { + data[i]= amlogic_cec_read_reg(CEC_RX_MSG_0_HEADER + i); + } + + amlogic_cec_msg_dump("RX", data, *count); + + ret = RX_DONE; + } + +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + aml_write_reg32(P_AO_CEC_INTR_CLR, aml_read_reg32(P_AO_CEC_INTR_CLR) | (1 << 2)); +#endif + amlogic_cec_write_reg(CEC_RX_MSG_CMD, valid_msg ? RX_ACK_NEXT : RX_ACK_CURRENT); + amlogic_cec_write_reg(CEC_RX_MSG_CMD, RX_NO_OP); + + return ret; +} + + +static void amlogic_cec_write_hw(const char *data, size_t count) +{ + int i; + + for (i = 0; i < count; ++i) + { + amlogic_cec_write_reg(CEC_TX_MSG_0_HEADER + i, data[i]); + } + amlogic_cec_write_reg(CEC_TX_MSG_LENGTH, count - 1); + amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_REQ_CURRENT); + + amlogic_cec_msg_dump("TX", data, count); +} + +unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) +{ +// unused, just to satisfy the linker + return log_addr; +} + +void cec_node_init(hdmitx_dev_t* hdmitx_device) +{ + unsigned long cec_phy_addr; + unsigned long spin_flags; + struct cec_rx_list *entry; + + cec_phy_addr = (((hdmitx_device->hdmi_info.vsdb_phy_addr.a) & 0xf) << 12) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.b) & 0xf) << 8) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.c) & 0xf) << 4) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.d) & 0xf) << 0); + + // If VSDB is not valid,use last or default physical address. + if (hdmitx_device->hdmi_info.vsdb_phy_addr.valid == 0) + { + amlogic_cec_log_dbg("no valid cec physical address\n"); + if (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) + { + amlogic_cec_log_dbg("use last physical address\n"); + } + else + { + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | 0x1000); + amlogic_cec_log_dbg("use default physical address\n"); + } + } + else + { + if (cec_global_info.my_node_index) + { + // prevent write operations + if (down_interruptible(&init_mutex)) + { + printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); + return; + } + hdmitx_device->cec_init_ready = 0; + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + + amlogic_cec_log_dbg("start reset\n"); +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + aml_write_reg32(P_HDMI_CTRL_PORT, aml_read_reg32(P_HDMI_CTRL_PORT)|(1<<16)); + hdmi_wr_reg(OTHER_BASE_ADDR+HDMI_OTHER_CTRL0, 0xc); //[3]cec_creg_sw_rst [2]cec_sys_sw_rst + +#if 0 + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_TX_CLEAR_BUF, 0x1); + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_RX_CLEAR_BUF, 0x1); + + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_TX_CLEAR_BUF, 0x0); + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_RX_CLEAR_BUF, 0x0); +#endif + + hdmi_wr_reg(OTHER_BASE_ADDR+HDMI_OTHER_CTRL0, 0x0); + aml_write_reg32(P_HDMI_CTRL_PORT, aml_read_reg32(P_HDMI_CTRL_PORT)&(~(1<<16))); + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_H, 0x00 ); + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_L, 0xf0 ); + +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + // regain rx interrupts + cec_enable_irq(); +#endif + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + hdmitx_device->cec_init_ready = 1; + + up(&init_mutex); + amlogic_cec_log_dbg("stop reset\n"); + } + + if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) + { + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); + amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + + if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) + { + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + } + else + { + // let the libCEC ask for new physical Address + entry->buffer[0] = 0xff; + entry->size = 1; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + amlogic_cec_log_dbg("trigger libCEC\n"); + wake_up_interruptible(&cec_rx_struct.waitq); + } + } + } + } +} + +static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) +{ + unsigned long spin_flags; + struct cec_rx_list *entry; + unsigned int tx_msg_state; + unsigned int rx_msg_state; + +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + udelay(100); //Delay execution a little. This fixes an issue when HDMI CEC stops working after a while. +#endif + + tx_msg_state = amlogic_cec_read_reg(CEC_TX_MSG_STATUS); + rx_msg_state = amlogic_cec_read_reg(CEC_RX_MSG_STATUS); + + amlogic_cec_log_dbg("cec msg status: rx: 0x%x; tx: 0x%x\n", rx_msg_state, tx_msg_state); + + if ((tx_msg_state == TX_DONE) || (tx_msg_state == TX_ERROR)) + { + amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_NO_OP); + + switch (tx_msg_state) { + case TX_ERROR : + amlogic_cec_set_tx_state(STATE_ERROR); + break; + case TX_DONE : + amlogic_cec_set_tx_state(STATE_DONE); + break; + } + wake_up_interruptible(&cec_tx_struct.waitq); + } + + if (rx_msg_state == RX_DONE) + { + + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + return IRQ_HANDLED; + } + + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + + if ((-1) == amlogic_cec_read_hw(entry->buffer, &entry->size)) + { + kfree(entry); + amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + return IRQ_HANDLED; + } + + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + wake_up_interruptible(&cec_rx_struct.waitq); + } + + return IRQ_HANDLED; +} + +static int amlogic_cec_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + if (atomic_read(&hdmi_on)) + { + amlogic_cec_log_dbg("do not allow multiple open for tvout cec\n"); + ret = -EBUSY; + } + else + { + atomic_inc(&hdmi_on); +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + if (request_irq(INT_HDMI_CEC, &amlogic_cec_irq_handler, + IRQF_SHARED, "amhdmitx-cec",(void *)hdmitx_device)) + { + amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); + return -EFAULT; + } +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + if (request_irq(INT_AO_CEC, &amlogic_cec_irq_handler, + IRQF_SHARED, "amhdmitx-aocec",(void *)hdmitx_device)) + { + amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); + return -EFAULT; + } +#endif + +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + cec_gpi_init(); +#endif + +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + aml_set_reg32_bits(P_PERIPHS_PIN_MUX_1, 1, 25, 1); + // Clear CEC Int. state and set CEC Int. mask + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR) | (1 << 23)); // Clear the interrupt + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) | (1 << 23)); // Enable the hdmi cec interrupt +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 +// GPIOAO_12 + aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 0, 14, 1); // bit[14]: AO_PWM_C pinmux //0xc8100014 + aml_set_reg32_bits(P_AO_RTI_PULL_UP_REG, 1, 12, 1); // bit[12]: enable AO_12 internal pull-up //0xc810002c + aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 1, 17, 1); // bit[17]: AO_CEC pinmux //0xc8100014 + 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); +#endif + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + + hdmitx_device->cec_init_ready = 1; + + } + return ret; +} + +static int amlogic_cec_release(struct inode *inode, struct file *file) +{ +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) & ~(1 << 23)); // Disable the hdmi cec interrupt + free_irq(INT_HDMI_CEC, (void *)hdmitx_device); +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + free_irq(INT_AO_CEC, (void *)hdmitx_device); +#endif + + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + + atomic_dec(&hdmi_on); + + return 0; +} + +static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + ssize_t retval; + unsigned long spin_flags; + struct cec_rx_list* entry = NULL; + + if (wait_event_interruptible(cec_rx_struct.waitq, + atomic_read(&cec_rx_struct.state) == STATE_DONE)) + { + amlogic_cec_log_dbg("error during wait on state change\n"); + return -ERESTARTSYS; + } + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + + entry = list_first_entry_or_null(&cec_rx_struct.list, struct cec_rx_list, list); + + if (entry == NULL || entry->size > count) + { + amlogic_cec_log_dbg("entry is NULL, or empty\n"); + retval = -1; + goto error_exit; + } + + if (copy_to_user(buffer, entry->buffer, entry->size)) + { + printk(KERN_ERR " copy_to_user() failed!\n"); + + retval = -EFAULT; + goto error_exit; + } + + retval = entry->size; + + amlogic_cec_set_rx_state(STATE_RX); + +error_exit: + if (entry != NULL) + { + list_del(&entry->list); + kfree(entry); + } + + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + return retval; +} + +static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int retval = count; + char data[CEC_TX_BUFF_SIZE]; + + /* check data size */ + if (count > CEC_TX_BUFF_SIZE || count == 0) + return -1; + + if (copy_from_user(data, buffer, count)) + { + printk(KERN_ERR " copy_from_user() failed!\n"); + return -EFAULT; + } + + amlogic_cec_set_tx_state(STATE_TX); + + // don't write if cec_node_init() is in progress + if (down_interruptible(&init_mutex)) + { + amlogic_cec_log_dbg("error during wait on state change\n"); + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + return -ERESTARTSYS; + } + + amlogic_cec_write_hw(data, count); + + if (wait_event_interruptible_timeout(cec_tx_struct.waitq, + atomic_read(&cec_tx_struct.state) != STATE_TX, 2 * HZ) <= 0) + { + amlogic_cec_log_dbg("error during wait on state change, resetting\n"); + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_ABORT); // stop cec tx for hw retry. + amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_NO_OP); + retval = -ERESTARTSYS; + goto error_exit; + } + + if (atomic_read(&cec_tx_struct.state) != STATE_DONE) + { + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + retval = -1; + } + +error_exit: + up(&init_mutex); + + return retval; +} + +static long amlogic_cec_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned char logical_addr; + unsigned int reg; + + switch(cmd) { + case CEC_IOC_SETLADDR: + if (get_user(logical_addr, (unsigned char __user *)arg)) + { + amlogic_cec_log_dbg("Failed to get logical addr from user\n"); + return -EFAULT; + } + + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | logical_addr); + cec_global_info.my_node_index = logical_addr; + /* + * use DEBUG_REG1 bit 16 ~ 31 to save logic address. + * So uboot can use this logic address directly + */ + reg = (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + reg |= ((unsigned int)logical_addr) << 16; + aml_write_reg32(P_AO_DEBUG_REG1, reg); + + amlogic_cec_log_dbg("amlogic_cec_ioctl: Set logical address: %d\n", logical_addr); + return 0; + + case CEC_IOC_GETPADDR: + amlogic_cec_log_dbg("amlogic_cec_ioctl: return physical address 0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + return aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff; + } + + return -EINVAL; +} + +static u32 amlogic_cec_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &cec_rx_struct.waitq, wait); + + if (atomic_read(&cec_rx_struct.state) == STATE_DONE) + { + return POLLIN | POLLRDNORM; + } + return 0; +} + +static const struct file_operations cec_fops = { + .owner = THIS_MODULE, + .open = amlogic_cec_open, + .release = amlogic_cec_release, + .read = amlogic_cec_read, + .write = amlogic_cec_write, + .unlocked_ioctl = amlogic_cec_ioctl, + .poll = amlogic_cec_poll, +}; + +static struct miscdevice cec_misc_device = { + .minor = CEC_MINOR, + .name = "AmlogicCEC", + .fops = &cec_fops, +}; + +static int amlogic_cec_init(void) +{ + int retval = 0; + extern hdmitx_dev_t * get_hdmitx_device(void); + + if (down_interruptible(&init_mutex)) + { + return -ERESTARTSYS; + } + + INIT_LIST_HEAD(&cec_rx_struct.list); + + printk("%s, Version: %s\n", banner, VERSION); + + hdmitx_device = get_hdmitx_device(); + amlogic_cec_log_dbg("CEC init\n"); + + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_H, 0x00 ); + hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_L, 0xf0 ); +#endif + + init_waitqueue_head(&cec_rx_struct.waitq); + + spin_lock_init(&cec_rx_struct.lock); + + init_waitqueue_head(&cec_tx_struct.waitq); + + spin_lock_init(&cec_tx_struct.lock); + + if (misc_register(&cec_misc_device)) + { + printk(KERN_WARNING " Couldn't register device 10, %d.\n", CEC_MINOR); + retval = -EBUSY; + } + + // release initial lock on init_mutex + up(&init_mutex); + + amlogic_cec_log_dbg("CEC init finished: %d\n", retval); + + return retval; +} + +static void amlogic_cec_exit(void) +{ + misc_deregister(&cec_misc_device); +} + +module_init(amlogic_cec_init); +module_exit(amlogic_cec_exit); + diff --git a/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c b/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c index 3e043bcf2014..2b11c72f22c3 100755 --- a/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c +++ b/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c @@ -542,6 +542,7 @@ static ssize_t store_disp_mode(struct device * dev, struct device_attribute *att return 16; } +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER /*cec attr*/ static ssize_t show_cec(struct device * dev, struct device_attribute *attr, char * buf) { @@ -589,6 +590,20 @@ static ssize_t show_cec_lang_config(struct device * dev, struct device_attribute pos+=snprintf(buf+pos, PAGE_SIZE, "%x\n",cec_global_info.cec_node_info[cec_global_info.my_node_index].menu_lang); return pos; } +#else + +extern unsigned long amlogic_cec_debug_flag; + +static ssize_t show_amlogic_cec_debug_config(struct device *dev, struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "amlogic_cec_debug:%lu\n", amlogic_cec_debug_flag); +} + +static ssize_t store_amlogic_cec_debug_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return kstrtoul(buf, 16, &amlogic_cec_debug_flag) ? 0 : count; +} +#endif /*aud_mode attr*/ static ssize_t show_aud_mode(struct device * dev, struct device_attribute *attr, char * buf) @@ -963,10 +978,14 @@ static DEVICE_ATTR(disp_cap_3d, S_IWUSR | S_IRUGO, show_disp_cap_3d, NULL); static DEVICE_ATTR(hdcp_ksv_info, S_IWUSR | S_IRUGO, show_hdcp_ksv_info, NULL); static DEVICE_ATTR(hpd_state, S_IWUSR | S_IRUGO, show_hpd_state, NULL); static DEVICE_ATTR(support_3d, S_IWUSR | S_IRUGO, show_support_3d, NULL); +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER static DEVICE_ATTR(cec, S_IWUSR | S_IRUGO, show_cec, store_cec); static DEVICE_ATTR(cec_config, S_IWUSR | S_IRUGO | S_IWGRP, show_cec_config, store_cec_config); //static DEVICE_ATTR(cec_config, S_IWUGO | S_IRUGO , NULL, store_cec_config); static DEVICE_ATTR(cec_lang_config, S_IWUSR | S_IRUGO | S_IWGRP, show_cec_lang_config, store_cec_lang_config); +#else +static DEVICE_ATTR(amlogic_cec_debug_config, S_IWUSR | S_IRUGO | S_IWGRP, show_amlogic_cec_debug_config, store_amlogic_cec_debug_config); +#endif /***************************** * hdmitx display client interface @@ -1582,7 +1601,9 @@ extern void register_hdmi_is_special_tv_func( int (*pfunc)(void) ); static int amhdmitx_probe(struct platform_device *pdev) { +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER extern struct switch_dev lang_dev; +#endif int r,ret=0; #ifdef CONFIG_USE_OF @@ -1643,9 +1664,13 @@ static int amhdmitx_probe(struct platform_device *pdev) ret=device_create_file(hdmitx_dev, &dev_attr_hdcp_ksv_info); ret=device_create_file(hdmitx_dev, &dev_attr_hpd_state); ret=device_create_file(hdmitx_dev, &dev_attr_support_3d); +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER ret=device_create_file(hdmitx_dev, &dev_attr_cec); ret=device_create_file(hdmitx_dev, &dev_attr_cec_config); ret=device_create_file(hdmitx_dev, &dev_attr_cec_lang_config); +#else + ret=device_create_file(hdmitx_dev, &dev_attr_amlogic_cec_debug_config); +#endif if (hdmitx_dev == NULL) { hdmi_print(ERR, SYS "device_create create error\n"); @@ -1732,7 +1757,9 @@ static int amhdmitx_probe(struct platform_device *pdev) } #endif switch_dev_register(&sdev); +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER switch_dev_register(&lang_dev); +#endif hdmitx_init_parameters(&hdmitx_device.hdmi_info); HDMITX_Meson_Init(&hdmitx_device); @@ -1771,8 +1798,9 @@ static int amhdmitx_remove(struct platform_device *pdev) device_remove_file(hdmitx_dev, &dev_attr_disp_cap_3d); device_remove_file(hdmitx_dev, &dev_attr_hpd_state); device_remove_file(hdmitx_dev, &dev_attr_support_3d); +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER device_remove_file(hdmitx_dev, &dev_attr_cec); - +#endif cdev_del(&hdmitx_device.cdev); device_destroy(hdmitx_class, hdmitx_id); diff --git a/drivers/amlogic/input/new_remote/remote_func.c b/drivers/amlogic/input/new_remote/remote_func.c index e36e272ed9fb..807c2556b4e8 100755 --- a/drivers/amlogic/input/new_remote/remote_func.c +++ b/drivers/amlogic/input/new_remote/remote_func.c @@ -525,6 +525,7 @@ int remote_hw_reprot_key(struct remote *remote_data) mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay[remote_data->map_num]+remote_data->repeat_delay[remote_data->map_num])); } else if((remote_data->frame_status & REPEARTFLAG) && remote_data->enable_repeat_falg) { //repeate key #ifdef CONFIG_AML_HDMI_TX +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER extern int rc_long_press_pwr_key; if((remote_data->repeat_release_code == 0x1a) && (!cec_repeat)) { rc_long_press_pwr_key = 1; @@ -533,6 +534,7 @@ int remote_hw_reprot_key(struct remote *remote_data) if(remote_data->repeat_release_code == 0x1a) cec_repeat--; +#endif #endif if (remote_data->repeat_enable) { repeat_count++; @@ -618,6 +620,7 @@ int remote_hw_nec_rca_2in1_reprot_key(struct remote *remote_data) mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay[remote_data->map_num]+remote_data->repeat_delay[remote_data->map_num])); } else if((remote_data->frame_status & REPEARTFLAG) && remote_data->enable_repeat_falg) { //repeate key #ifdef CONFIG_AML_HDMI_TX +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER extern int rc_long_press_pwr_key; if((remote_data->repeat_release_code == 0x1a) && (!cec_repeat)) { rc_long_press_pwr_key = 1; @@ -626,6 +629,7 @@ int remote_hw_nec_rca_2in1_reprot_key(struct remote *remote_data) if(remote_data->repeat_release_code == 0x1a) cec_repeat--; +#endif #endif if (remote_data->repeat_enable) { repeat_count++; @@ -700,6 +704,7 @@ int remote_hw_nec_toshiba_2in1_reprot_key(struct remote *remote_data) mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay[remote_data->map_num]+remote_data->repeat_delay[remote_data->map_num])); } else if((remote_data->frame_status & REPEARTFLAG) && remote_data->enable_repeat_falg) { //repeate key #ifdef CONFIG_AML_HDMI_TX +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER extern int rc_long_press_pwr_key; if((remote_data->repeat_release_code == 0x1a) && (!cec_repeat)) { rc_long_press_pwr_key = 1; @@ -708,6 +713,7 @@ int remote_hw_nec_toshiba_2in1_reprot_key(struct remote *remote_data) if(remote_data->repeat_release_code == 0x1a) cec_repeat--; +#endif #endif if (remote_data->repeat_enable) { repeat_count++; diff --git a/drivers/amlogic/input/remote/am_remote.c b/drivers/amlogic/input/remote/am_remote.c index c824efb2fe7e..01f0bfbaaedf 100755 --- a/drivers/amlogic/input/remote/am_remote.c +++ b/drivers/amlogic/input/remote/am_remote.c @@ -390,6 +390,7 @@ static inline int remote_hw_reprot_key(struct remote *remote_data) } #ifdef CONFIG_AML_HDMI_TX #ifdef CONFIG_ARCH_MESON6 +#ifndef CONFIG_AML_HDMI_TX_NEW_CEC_DRIVER //printk("last_scan_code:%x\n", last_scan_code); if((((scan_code >> 16) & 0xff) == 0x1a) && (!cec_repeat)) { extern int rc_long_press_pwr_key; @@ -400,6 +401,7 @@ static inline int remote_hw_reprot_key(struct remote *remote_data) if(((scan_code >> 16) & 0xff) == 0x1a) cec_repeat--; #endif +#endif #endif if (remote_data->repeat_enable) { if ((remote_data->repeat_tick < jiffies)&&(repeat_flag == 1)) { From 62214a7efb79f180c932dd0d42b0880ddc35d281 Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 10 Apr 2016 16:58:40 +0200 Subject: [PATCH 02/21] amlogic_cec: add cec_late_timer Sometimes there are messages waiting in rx queue that are not visible for methods checking the buffer. This commits implements hrtimer that checks buffer contents every 384ms. This is in line with Amlogic's CEC driver. --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index a3556b31ec1b..0839c6f8d556 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -102,6 +102,8 @@ static struct cec_rx_struct cec_rx_struct; static struct cec_tx_struct cec_tx_struct; +static struct hrtimer cec_late_timer; + static atomic_t hdmi_on = ATOMIC_INIT(0); cec_global_info_t cec_global_info; @@ -217,6 +219,48 @@ unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) return log_addr; } +static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) +{ + unsigned long spin_flags; + struct cec_rx_list *entry; + + if (cec_rx_buf_check()) + { + /* + * start another check if rx buffer is full + */ + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + return HRTIMER_NORESTART; + } + + INIT_LIST_HEAD(&entry->list); + + if ((-1) == amlogic_cec_read_hw(entry->buffer, &entry->size)) + { + kfree(entry); + amlogic_cec_log_dbg("buffer got unrecorgnized msg\n"); + cec_rx_buf_clear(); + } + else + { + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + wake_up_interruptible(&cec_rx_struct.waitq); + } + } + if (atomic_read(&hdmi_on)) + { + hrtimer_start(&cec_late_timer, ktime_set(0, 384*1000*1000), HRTIMER_MODE_REL); + } + + return HRTIMER_NORESTART; +} + void cec_node_init(hdmitx_dev_t* hdmitx_device) { unsigned long cec_phy_addr; @@ -430,6 +474,8 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) #endif amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + hrtimer_start(&cec_late_timer, ktime_set(0, 384*1000*1000), HRTIMER_MODE_REL); + hdmitx_device->cec_init_ready = 1; } @@ -653,6 +699,9 @@ static int amlogic_cec_init(void) retval = -EBUSY; } + hrtimer_init(&cec_late_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cec_late_timer.function = cec_late_check_rx_buffer; + // release initial lock on init_mutex up(&init_mutex); From 59eec3e45e0f3dd41b195c9f2fe9c483f4e46892 Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 10 Apr 2016 13:46:48 +0200 Subject: [PATCH 03/21] amlogic_cec: rely on Amlogic HW driver to transmit messages --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 0839c6f8d556..9ee5721d4e7d 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -574,15 +574,13 @@ static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, return -ERESTARTSYS; } - amlogic_cec_write_hw(data, count); + cec_ll_tx(data, count); if (wait_event_interruptible_timeout(cec_tx_struct.waitq, atomic_read(&cec_tx_struct.state) != STATE_TX, 2 * HZ) <= 0) { amlogic_cec_log_dbg("error during wait on state change, resetting\n"); printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); - amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_ABORT); // stop cec tx for hw retry. - amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_NO_OP); retval = -ERESTARTSYS; goto error_exit; } From b0f4c8333d26970d320b880843d058ea6b7a4980 Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 10 Apr 2016 15:59:40 +0200 Subject: [PATCH 04/21] amlogic_cec: rely on Amlogic HW driver to receive messages --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 9ee5721d4e7d..fdb7c813282d 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -237,7 +237,7 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) INIT_LIST_HEAD(&entry->list); - if ((-1) == amlogic_cec_read_hw(entry->buffer, &entry->size)) + if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) { kfree(entry); amlogic_cec_log_dbg("buffer got unrecorgnized msg\n"); @@ -405,7 +405,7 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - if ((-1) == amlogic_cec_read_hw(entry->buffer, &entry->size)) + if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) { kfree(entry); amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); From 63adc1a7c52f3a1412c86169c1664a11b8f804ca Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 10 Apr 2016 16:08:42 +0200 Subject: [PATCH 05/21] amlogic_cec: delete unused read and write methods --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 53 ---------------------- 1 file changed, 53 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index fdb7c813282d..fb035bfffad5 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -160,59 +160,6 @@ static void amlogic_cec_write_reg(unsigned int reg, unsigned int value) #endif } -static int amlogic_cec_read_hw(unsigned char *data, unsigned char *count) -{ - int ret = -1; - int valid_msg; - int rx_msg_status; - int rx_num_msg; - - rx_msg_status = amlogic_cec_read_reg(CEC_RX_MSG_STATUS); - rx_num_msg = amlogic_cec_read_reg(CEC_RX_NUM_MSG); - - amlogic_cec_log_dbg("rx_msg_status: %d, rx_num_msg: %d\n", rx_msg_status, rx_num_msg); - - valid_msg = (RX_DONE == rx_msg_status) && (1 == rx_num_msg); - - if (valid_msg) - { - int i; - - *count = amlogic_cec_read_reg(CEC_RX_MSG_LENGTH) + 1; - for (i = 0; i < (*count) && i < CEC_RX_BUFF_SIZE; ++i) - { - data[i]= amlogic_cec_read_reg(CEC_RX_MSG_0_HEADER + i); - } - - amlogic_cec_msg_dump("RX", data, *count); - - ret = RX_DONE; - } - -#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 - aml_write_reg32(P_AO_CEC_INTR_CLR, aml_read_reg32(P_AO_CEC_INTR_CLR) | (1 << 2)); -#endif - amlogic_cec_write_reg(CEC_RX_MSG_CMD, valid_msg ? RX_ACK_NEXT : RX_ACK_CURRENT); - amlogic_cec_write_reg(CEC_RX_MSG_CMD, RX_NO_OP); - - return ret; -} - - -static void amlogic_cec_write_hw(const char *data, size_t count) -{ - int i; - - for (i = 0; i < count; ++i) - { - amlogic_cec_write_reg(CEC_TX_MSG_0_HEADER + i, data[i]); - } - amlogic_cec_write_reg(CEC_TX_MSG_LENGTH, count - 1); - amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_REQ_CURRENT); - - amlogic_cec_msg_dump("TX", data, count); -} - unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) { // unused, just to satisfy the linker From 1d6dc4e44b9ac20dfffc9fda5a8f8999b8febbc9 Mon Sep 17 00:00:00 2001 From: kszaq Date: Sat, 9 Apr 2016 23:47:59 +0200 Subject: [PATCH 06/21] amlogic_cec: add tx_irq_handle for MESON8 When using Amlogic HW driver to transmit message, this is needed to send TX_ABORT and TX_NO_OP. --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index fb035bfffad5..fbacd363806c 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -326,8 +326,6 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) if ((tx_msg_state == TX_DONE) || (tx_msg_state == TX_ERROR)) { - amlogic_cec_write_reg(CEC_TX_MSG_CMD, TX_NO_OP); - switch (tx_msg_state) { case TX_ERROR : amlogic_cec_set_tx_state(STATE_ERROR); @@ -339,6 +337,14 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) wake_up_interruptible(&cec_tx_struct.waitq); } +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + if (aml_read_reg32(P_AO_CEC_INTR_STAT) & (1<<1)) + { // aocec tx intr + tx_irq_handle(); + return IRQ_HANDLED; + } +#endif + if (rx_msg_state == RX_DONE) { From 0ddc2050aa056013043735c70f04db639069873d Mon Sep 17 00:00:00 2001 From: kszaq Date: Tue, 12 Apr 2016 00:04:32 +0200 Subject: [PATCH 07/21] amlogic_cec: modify rx irq handling - do not lock if unrecognized message received - clear rx buffer on unrecognized message --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index fbacd363806c..805b2b99b8ad 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -356,16 +356,15 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) INIT_LIST_HEAD(&entry->list); - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) { kfree(entry); amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + cec_rx_buf_clear(); return IRQ_HANDLED; } + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); list_add_tail(&entry->list, &cec_rx_struct.list); amlogic_cec_set_rx_state(STATE_DONE); spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); From 9e614681724e7e7e95d6202c626458271b4f0ce3 Mon Sep 17 00:00:00 2001 From: kszaq Date: Mon, 18 Apr 2016 00:13:23 +0200 Subject: [PATCH 08/21] amlogic_cec: move INIT_LIST_HEAD --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 805b2b99b8ad..5cad7881981a 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -182,8 +182,6 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) return HRTIMER_NORESTART; } - INIT_LIST_HEAD(&entry->list); - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) { kfree(entry); @@ -192,6 +190,7 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) } else { + INIT_LIST_HEAD(&entry->list); spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); list_add_tail(&entry->list, &cec_rx_struct.list); amlogic_cec_set_rx_state(STATE_DONE); @@ -354,8 +353,6 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) return IRQ_HANDLED; } - INIT_LIST_HEAD(&entry->list); - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) { kfree(entry); @@ -364,6 +361,8 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) return IRQ_HANDLED; } + INIT_LIST_HEAD(&entry->list); + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); list_add_tail(&entry->list, &cec_rx_struct.list); amlogic_cec_set_rx_state(STATE_DONE); From 5dfcdd57958ba81db5b72be5ca23679e05902f78 Mon Sep 17 00:00:00 2001 From: kszaq Date: Tue, 19 Apr 2016 06:43:34 +0200 Subject: [PATCH 09/21] amlogic_cec: add hw reset on driver reopen --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 5cad7881981a..ac6e7c3fec50 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -246,28 +246,13 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); amlogic_cec_log_dbg("start reset\n"); -#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 - aml_write_reg32(P_HDMI_CTRL_PORT, aml_read_reg32(P_HDMI_CTRL_PORT)|(1<<16)); - hdmi_wr_reg(OTHER_BASE_ADDR+HDMI_OTHER_CTRL0, 0xc); //[3]cec_creg_sw_rst [2]cec_sys_sw_rst - -#if 0 - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_TX_CLEAR_BUF, 0x1); - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_RX_CLEAR_BUF, 0x1); - - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_TX_CLEAR_BUF, 0x0); - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_RX_CLEAR_BUF, 0x0); -#endif - - hdmi_wr_reg(OTHER_BASE_ADDR+HDMI_OTHER_CTRL0, 0x0); - aml_write_reg32(P_HDMI_CTRL_PORT, aml_read_reg32(P_HDMI_CTRL_PORT)&(~(1<<16))); - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_H, 0x00 ); - hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_L, 0xf0 ); - -#endif -#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 // regain rx interrupts cec_enable_irq(); -#endif + if (cec_rx_buf_check()) { + cec_rx_buf_clear(); + } + cec_hw_reset(); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); hdmitx_device->cec_init_ready = 1; From 5386ba8863d99cb950fe772d50b0b814ff32175b Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 10 Apr 2016 15:06:52 +0200 Subject: [PATCH 10/21] amlogic_cec: reset_hw on wait error --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index ac6e7c3fec50..f5f1114f6340 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -517,6 +517,7 @@ static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, { amlogic_cec_log_dbg("error during wait on state change, resetting\n"); printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + cec_hw_reset(); retval = -ERESTARTSYS; goto error_exit; } From c0b38c656b532ef1cedf5824be0214df7966cc82 Mon Sep 17 00:00:00 2001 From: kszaq Date: Mon, 25 Apr 2016 12:51:01 +0200 Subject: [PATCH 11/21] amlogic_cec: don't delete list entry if copy_to_user failed --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index f5f1114f6340..cc1eb3f22c23 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -465,20 +465,21 @@ static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, printk(KERN_ERR " copy_to_user() failed!\n"); retval = -EFAULT; - goto error_exit; + goto error_exit2; } retval = entry->size; +error_exit: amlogic_cec_set_rx_state(STATE_RX); -error_exit: if (entry != NULL) { list_del(&entry->list); kfree(entry); } +error_exit2: spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); return retval; From 7318e9649143970feb49dec7d31e9a16a65de1a3 Mon Sep 17 00:00:00 2001 From: kszaq Date: Mon, 25 Apr 2016 12:56:30 +0200 Subject: [PATCH 12/21] amlogic_cec: don't wait for interrupt if there are messages in rx queue --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index cc1eb3f22c23..2b3ee52eee22 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -471,14 +471,17 @@ static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, retval = entry->size; error_exit: - amlogic_cec_set_rx_state(STATE_RX); - if (entry != NULL) { list_del(&entry->list); kfree(entry); } + if (list_empty(&cec_rx_struct.list)) + { + amlogic_cec_set_rx_state(STATE_RX); + } + error_exit2: spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); @@ -572,6 +575,11 @@ static long amlogic_cec_ioctl(struct file *file, unsigned int cmd, static u32 amlogic_cec_poll(struct file *file, poll_table *wait) { + if (atomic_read(&cec_rx_struct.state) == STATE_DONE) + { + return POLLIN | POLLRDNORM; + } + poll_wait(file, &cec_rx_struct.waitq, wait); if (atomic_read(&cec_rx_struct.state) == STATE_DONE) From 3876edb756206f0f6ef6b5a07a5251ce7a35e44e Mon Sep 17 00:00:00 2001 From: kszaq Date: Sat, 23 Apr 2016 10:52:47 +0200 Subject: [PATCH 13/21] amlogic_cec: move irq init/release to module init/exit --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 49 +++++++++++----------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 2b3ee52eee22..080b8fd6bbf7 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -371,23 +371,6 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) else { atomic_inc(&hdmi_on); -#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 - if (request_irq(INT_HDMI_CEC, &amlogic_cec_irq_handler, - IRQF_SHARED, "amhdmitx-cec",(void *)hdmitx_device)) - { - amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); - return -EFAULT; - } -#endif -#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 - if (request_irq(INT_AO_CEC, &amlogic_cec_irq_handler, - IRQF_SHARED, "amhdmitx-aocec",(void *)hdmitx_device)) - { - amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); - return -EFAULT; - } -#endif - #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 cec_gpi_init(); #endif @@ -420,14 +403,6 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) static int amlogic_cec_release(struct inode *inode, struct file *file) { -#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 - aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) & ~(1 << 23)); // Disable the hdmi cec interrupt - free_irq(INT_HDMI_CEC, (void *)hdmitx_device); -#endif -#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 - free_irq(INT_AO_CEC, (void *)hdmitx_device); -#endif - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); atomic_dec(&hdmi_on); @@ -646,6 +621,23 @@ static int amlogic_cec_init(void) hrtimer_init(&cec_late_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); cec_late_timer.function = cec_late_check_rx_buffer; +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + if (request_irq(INT_HDMI_CEC, &amlogic_cec_irq_handler, + IRQF_SHARED, "amhdmitx-cec",(void *)hdmitx_device)) + { + amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); + return -EFAULT; + } +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + if (request_irq(INT_AO_CEC, &amlogic_cec_irq_handler, + IRQF_SHARED, "amhdmitx-aocec",(void *)hdmitx_device)) + { + amlogic_cec_log_dbg("Can't register IRQ %d\n",INT_HDMI_CEC); + return -EFAULT; + } +#endif + // release initial lock on init_mutex up(&init_mutex); @@ -656,6 +648,13 @@ static int amlogic_cec_init(void) static void amlogic_cec_exit(void) { +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) & ~(1 << 23)); // Disable the hdmi cec interrupt + free_irq(INT_HDMI_CEC, (void *)hdmitx_device); +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + free_irq(INT_AO_CEC, (void *)hdmitx_device); +#endif misc_deregister(&cec_misc_device); } From 7cfda7287b860e964878b64afc7dae34011172e1 Mon Sep 17 00:00:00 2001 From: kszaq Date: Thu, 28 Apr 2016 10:03:39 +0200 Subject: [PATCH 14/21] amlogic_cec: fix tabbing --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 348 ++++++++++----------- 1 file changed, 174 insertions(+), 174 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 080b8fd6bbf7..58ed35af6e91 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -52,7 +52,7 @@ bool cec_msg_dbg_en = 1; #ifndef amlogic_cec_log_dbg #define amlogic_cec_log_dbg(fmt, ...) \ if (amlogic_cec_debug_flag) \ - printk(KERN_INFO "[%s] %s(): " fmt, DRV_NAME, __func__, ##__VA_ARGS__) + printk(KERN_INFO "[%s] %s(): " fmt, DRV_NAME, __func__, ##__VA_ARGS__) #endif #define CEC_IOC_MAGIC 'c' @@ -60,7 +60,7 @@ bool cec_msg_dbg_en = 1; #define CEC_IOC_GETPADDR _IO(CEC_IOC_MAGIC, 1) #define VERSION "0.0.1" /* Driver version number */ -#define CEC_MINOR 243 /* Major 10, Minor 242, /dev/cec */ +#define CEC_MINOR 243 /* Major 10, Minor 242, /dev/cec */ /* CEC Rx buffer size */ #define CEC_RX_BUFF_SIZE 16 @@ -70,29 +70,29 @@ bool cec_msg_dbg_en = 1; static DEFINE_SEMAPHORE(init_mutex); struct cec_rx_list { - u8 buffer[CEC_RX_BUFF_SIZE]; - unsigned char size; - struct list_head list; + u8 buffer[CEC_RX_BUFF_SIZE]; + unsigned char size; + struct list_head list; }; struct cec_rx_struct { - spinlock_t lock; - wait_queue_head_t waitq; - atomic_t state; - struct list_head list; + spinlock_t lock; + wait_queue_head_t waitq; + atomic_t state; + struct list_head list; }; struct cec_tx_struct { - spinlock_t lock; - wait_queue_head_t waitq; - atomic_t state; + spinlock_t lock; + wait_queue_head_t waitq; + atomic_t state; }; enum cec_state { - STATE_RX, - STATE_TX, - STATE_DONE, - STATE_ERROR + STATE_RX, + STATE_TX, + STATE_DONE, + STATE_ERROR }; static char banner[] __initdata = @@ -122,22 +122,22 @@ static void amlogic_cec_set_tx_state(enum cec_state state) static void amlogic_cec_msg_dump(char * msg_tag, const unsigned char *data, unsigned char count) { - int i; - int pos; - unsigned char msg_log_buf[128] = { 0 }; - - if (amlogic_cec_debug_flag == 1) - { - pos = 0; - pos += sprintf(msg_log_buf + pos, "msg %s len: %d dat: ", msg_tag, count); - for (i = 0; i < count; ++i) - { - pos += sprintf(msg_log_buf + pos, "%02x ", data[i]); - } - pos += sprintf(msg_log_buf + pos, "\n"); - msg_log_buf[pos] = '\0'; - hdmi_print(INF, "[amlogic_cec] dump: %s", msg_log_buf); - } + int i; + int pos; + unsigned char msg_log_buf[128] = { 0 }; + + if (amlogic_cec_debug_flag == 1) + { + pos = 0; + pos += sprintf(msg_log_buf + pos, "msg %s len: %d dat: ", msg_tag, count); + for (i = 0; i < count; ++i) + { + pos += sprintf(msg_log_buf + pos, "%02x ", data[i]); + } + pos += sprintf(msg_log_buf + pos, "\n"); + msg_log_buf[pos] = '\0'; + hdmi_print(INF, "[amlogic_cec] dump: %s", msg_log_buf); + } } static unsigned int amlogic_cec_read_reg(unsigned int reg) @@ -162,8 +162,8 @@ static void amlogic_cec_write_reg(unsigned int reg, unsigned int value) unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) { -// unused, just to satisfy the linker - return log_addr; + // unused, just to satisfy the linker + return log_addr; } static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) @@ -176,28 +176,28 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) /* * start another check if rx buffer is full */ - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - return HRTIMER_NORESTART; - } - - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) - { - kfree(entry); - amlogic_cec_log_dbg("buffer got unrecorgnized msg\n"); - cec_rx_buf_clear(); - } - else - { - INIT_LIST_HEAD(&entry->list); - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - wake_up_interruptible(&cec_rx_struct.waitq); - } + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + return HRTIMER_NORESTART; + } + + if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) + { + kfree(entry); + amlogic_cec_log_dbg("buffer got unrecorgnized msg\n"); + cec_rx_buf_clear(); + } + else + { + INIT_LIST_HEAD(&entry->list); + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + wake_up_interruptible(&cec_rx_struct.waitq); + } } if (atomic_read(&hdmi_on)) { @@ -221,33 +221,33 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) // If VSDB is not valid,use last or default physical address. if (hdmitx_device->hdmi_info.vsdb_phy_addr.valid == 0) { - amlogic_cec_log_dbg("no valid cec physical address\n"); - if (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) - { - amlogic_cec_log_dbg("use last physical address\n"); - } - else - { - aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | 0x1000); - amlogic_cec_log_dbg("use default physical address\n"); - } + amlogic_cec_log_dbg("no valid cec physical address\n"); + if (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) + { + amlogic_cec_log_dbg("use last physical address\n"); + } + else + { + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | 0x1000); + amlogic_cec_log_dbg("use default physical address\n"); + } } else { - if (cec_global_info.my_node_index) - { - // prevent write operations - if (down_interruptible(&init_mutex)) - { - printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); - return; - } - hdmitx_device->cec_init_ready = 0; + if (cec_global_info.my_node_index) + { + // prevent write operations + if (down_interruptible(&init_mutex)) + { + printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); + return; + } + hdmitx_device->cec_init_ready = 0; spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - amlogic_cec_log_dbg("start reset\n"); - // regain rx interrupts - cec_enable_irq(); + amlogic_cec_log_dbg("start reset\n"); + // regain rx interrupts + cec_enable_irq(); if (cec_rx_buf_check()) { cec_rx_buf_clear(); } @@ -257,38 +257,38 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) hdmitx_device->cec_init_ready = 1; - up(&init_mutex); - amlogic_cec_log_dbg("stop reset\n"); - } - - if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) - { - aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); - amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); - - if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) - { - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - } - else - { - // let the libCEC ask for new physical Address - entry->buffer[0] = 0xff; - entry->size = 1; - INIT_LIST_HEAD(&entry->list); - - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - amlogic_cec_log_dbg("trigger libCEC\n"); - wake_up_interruptible(&cec_rx_struct.waitq); - } - } - } + up(&init_mutex); + amlogic_cec_log_dbg("stop reset\n"); + } + + if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) + { + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); + amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + + if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) + { + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + } + else + { + // let the libCEC ask for new physical Address + entry->buffer[0] = 0xff; + entry->size = 1; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + amlogic_cec_log_dbg("trigger libCEC\n"); + wake_up_interruptible(&cec_rx_struct.waitq); + } + } + } } } @@ -310,15 +310,15 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) if ((tx_msg_state == TX_DONE) || (tx_msg_state == TX_ERROR)) { - switch (tx_msg_state) { - case TX_ERROR : - amlogic_cec_set_tx_state(STATE_ERROR); - break; - case TX_DONE : - amlogic_cec_set_tx_state(STATE_DONE); - break; - } - wake_up_interruptible(&cec_tx_struct.waitq); + switch (tx_msg_state) { + case TX_ERROR : + amlogic_cec_set_tx_state(STATE_ERROR); + break; + case TX_DONE : + amlogic_cec_set_tx_state(STATE_DONE); + break; + } + wake_up_interruptible(&cec_tx_struct.waitq); } #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 @@ -332,28 +332,28 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) if (rx_msg_state == RX_DONE) { - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - return IRQ_HANDLED; - } + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + return IRQ_HANDLED; + } - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) - { - kfree(entry); - amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); - cec_rx_buf_clear(); - return IRQ_HANDLED; - } + if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) + { + kfree(entry); + amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); + cec_rx_buf_clear(); + return IRQ_HANDLED; + } - INIT_LIST_HEAD(&entry->list); + INIT_LIST_HEAD(&entry->list); - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - wake_up_interruptible(&cec_rx_struct.waitq); + wake_up_interruptible(&cec_rx_struct.waitq); } return IRQ_HANDLED; @@ -365,12 +365,12 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) if (atomic_read(&hdmi_on)) { - amlogic_cec_log_dbg("do not allow multiple open for tvout cec\n"); - ret = -EBUSY; + amlogic_cec_log_dbg("do not allow multiple open for tvout cec\n"); + ret = -EBUSY; } else { - atomic_inc(&hdmi_on); + atomic_inc(&hdmi_on); #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 cec_gpi_init(); #endif @@ -411,17 +411,17 @@ static int amlogic_cec_release(struct inode *inode, struct file *file) } static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t retval; unsigned long spin_flags; struct cec_rx_list* entry = NULL; if (wait_event_interruptible(cec_rx_struct.waitq, - atomic_read(&cec_rx_struct.state) == STATE_DONE)) + atomic_read(&cec_rx_struct.state) == STATE_DONE)) { - amlogic_cec_log_dbg("error during wait on state change\n"); - return -ERESTARTSYS; + amlogic_cec_log_dbg("error during wait on state change\n"); + return -ERESTARTSYS; } spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); @@ -430,17 +430,17 @@ static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, if (entry == NULL || entry->size > count) { - amlogic_cec_log_dbg("entry is NULL, or empty\n"); - retval = -1; - goto error_exit; + amlogic_cec_log_dbg("entry is NULL, or empty\n"); + retval = -1; + goto error_exit; } if (copy_to_user(buffer, entry->buffer, entry->size)) { - printk(KERN_ERR " copy_to_user() failed!\n"); + printk(KERN_ERR " copy_to_user() failed!\n"); - retval = -EFAULT; - goto error_exit2; + retval = -EFAULT; + goto error_exit2; } retval = entry->size; @@ -448,13 +448,13 @@ static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, error_exit: if (entry != NULL) { - list_del(&entry->list); - kfree(entry); + list_del(&entry->list); + kfree(entry); } if (list_empty(&cec_rx_struct.list)) { - amlogic_cec_set_rx_state(STATE_RX); + amlogic_cec_set_rx_state(STATE_RX); } error_exit2: @@ -464,19 +464,19 @@ static ssize_t amlogic_cec_read(struct file *file, char __user *buffer, } static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { int retval = count; char data[CEC_TX_BUFF_SIZE]; /* check data size */ if (count > CEC_TX_BUFF_SIZE || count == 0) - return -1; + return -1; if (copy_from_user(data, buffer, count)) { - printk(KERN_ERR " copy_from_user() failed!\n"); - return -EFAULT; + printk(KERN_ERR " copy_from_user() failed!\n"); + return -EFAULT; } amlogic_cec_set_tx_state(STATE_TX); @@ -484,9 +484,9 @@ static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, // don't write if cec_node_init() is in progress if (down_interruptible(&init_mutex)) { - amlogic_cec_log_dbg("error during wait on state change\n"); - printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); - return -ERESTARTSYS; + amlogic_cec_log_dbg("error during wait on state change\n"); + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + return -ERESTARTSYS; } cec_ll_tx(data, count); @@ -494,17 +494,17 @@ static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, if (wait_event_interruptible_timeout(cec_tx_struct.waitq, atomic_read(&cec_tx_struct.state) != STATE_TX, 2 * HZ) <= 0) { - amlogic_cec_log_dbg("error during wait on state change, resetting\n"); - printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); - cec_hw_reset(); - retval = -ERESTARTSYS; - goto error_exit; + amlogic_cec_log_dbg("error during wait on state change, resetting\n"); + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + cec_hw_reset(); + retval = -ERESTARTSYS; + goto error_exit; } if (atomic_read(&cec_tx_struct.state) != STATE_DONE) { - printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); - retval = -1; + printk(KERN_ERR "[amlogic] ##### cec write error! #####\n"); + retval = -1; } error_exit: @@ -514,7 +514,7 @@ static ssize_t amlogic_cec_write(struct file *file, const char __user *buffer, } static long amlogic_cec_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg) { unsigned char logical_addr; unsigned int reg; @@ -542,7 +542,7 @@ static long amlogic_cec_ioctl(struct file *file, unsigned int cmd, case CEC_IOC_GETPADDR: amlogic_cec_log_dbg("amlogic_cec_ioctl: return physical address 0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); - return aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff; + return aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff; } return -EINVAL; @@ -552,14 +552,14 @@ static u32 amlogic_cec_poll(struct file *file, poll_table *wait) { if (atomic_read(&cec_rx_struct.state) == STATE_DONE) { - return POLLIN | POLLRDNORM; + return POLLIN | POLLRDNORM; } poll_wait(file, &cec_rx_struct.waitq, wait); if (atomic_read(&cec_rx_struct.state) == STATE_DONE) { - return POLLIN | POLLRDNORM; + return POLLIN | POLLRDNORM; } return 0; } @@ -587,7 +587,7 @@ static int amlogic_cec_init(void) if (down_interruptible(&init_mutex)) { - return -ERESTARTSYS; + return -ERESTARTSYS; } INIT_LIST_HEAD(&cec_rx_struct.list); @@ -614,8 +614,8 @@ static int amlogic_cec_init(void) if (misc_register(&cec_misc_device)) { - printk(KERN_WARNING " Couldn't register device 10, %d.\n", CEC_MINOR); - retval = -EBUSY; + printk(KERN_WARNING " Couldn't register device 10, %d.\n", CEC_MINOR); + retval = -EBUSY; } hrtimer_init(&cec_late_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); From 326f4991c428770640043ed064a2479e53e3798e Mon Sep 17 00:00:00 2001 From: kszaq Date: Thu, 28 Apr 2016 12:41:59 +0200 Subject: [PATCH 15/21] amlogic_cec: move common read methods to one function --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 73 +++++++++------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 58ed35af6e91..2c96af21ca97 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -160,6 +160,35 @@ static void amlogic_cec_write_reg(unsigned int reg, unsigned int value) #endif } +static int amlogic_cec_read_hw() +{ + int retval = 0; + + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + retval = -1; + } + + if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) + { + kfree(entry); + cec_rx_buf_clear(); + } + else + { + INIT_LIST_HEAD(&entry->list); + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + wake_up_interruptible(&cec_rx_struct.waitq); + } + + return retval; +} + unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) { // unused, just to satisfy the linker @@ -176,28 +205,10 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) /* * start another check if rx buffer is full */ - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + if ((-1) == amlogic_cec_read_hw()) { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); return HRTIMER_NORESTART; } - - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) - { - kfree(entry); - amlogic_cec_log_dbg("buffer got unrecorgnized msg\n"); - cec_rx_buf_clear(); - } - else - { - INIT_LIST_HEAD(&entry->list); - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - wake_up_interruptible(&cec_rx_struct.waitq); - } } if (atomic_read(&hdmi_on)) { @@ -331,29 +342,7 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) if (rx_msg_state == RX_DONE) { - - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - return IRQ_HANDLED; - } - - if ((-1) == cec_ll_rx(entry->buffer, &entry->size)) - { - kfree(entry); - amlogic_cec_log_dbg("amlogic_cec_irq_handler: nothing to read\n"); - cec_rx_buf_clear(); - return IRQ_HANDLED; - } - - INIT_LIST_HEAD(&entry->list); - - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - wake_up_interruptible(&cec_rx_struct.waitq); + amlogic_cec_read_hw(); } return IRQ_HANDLED; From 6c7f61f18e398531f99d2b55edc1d9d819853475 Mon Sep 17 00:00:00 2001 From: kszaq Date: Sun, 24 Apr 2016 00:45:06 +0200 Subject: [PATCH 16/21] amlogic_cec: perform cec_node_init later Clear RX buffer and reset HW on driver resume --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 133 +++++++++++---------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 2c96af21ca97..021d17f430d9 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -163,6 +163,8 @@ static void amlogic_cec_write_reg(unsigned int reg, unsigned int value) static int amlogic_cec_read_hw() { int retval = 0; + unsigned long spin_flags; + struct cec_rx_list *entry; if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) { @@ -197,9 +199,6 @@ unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) { - unsigned long spin_flags; - struct cec_rx_list *entry; - if (cec_rx_buf_check()) { /* @@ -224,79 +223,83 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) unsigned long spin_flags; struct cec_rx_list *entry; - cec_phy_addr = (((hdmitx_device->hdmi_info.vsdb_phy_addr.a) & 0xf) << 12) - | (((hdmitx_device->hdmi_info.vsdb_phy_addr.b) & 0xf) << 8) - | (((hdmitx_device->hdmi_info.vsdb_phy_addr.c) & 0xf) << 4) - | (((hdmitx_device->hdmi_info.vsdb_phy_addr.d) & 0xf) << 0); - - // If VSDB is not valid,use last or default physical address. - if (hdmitx_device->hdmi_info.vsdb_phy_addr.valid == 0) + if (atomic_read(&hdmi_on)) { - amlogic_cec_log_dbg("no valid cec physical address\n"); - if (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) + cec_phy_addr = (((hdmitx_device->hdmi_info.vsdb_phy_addr.a) & 0xf) << 12) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.b) & 0xf) << 8) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.c) & 0xf) << 4) + | (((hdmitx_device->hdmi_info.vsdb_phy_addr.d) & 0xf) << 0); + + // If VSDB is not valid,use last or default physical address. + if (hdmitx_device->hdmi_info.vsdb_phy_addr.valid == 0) { - amlogic_cec_log_dbg("use last physical address\n"); + amlogic_cec_log_dbg("no valid cec physical address\n"); + if (aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) + { + amlogic_cec_log_dbg("use last physical address\n"); + } + else + { + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | 0x1000); + amlogic_cec_log_dbg("use default physical address\n"); + } } else { - aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | 0x1000); - amlogic_cec_log_dbg("use default physical address\n"); - } - } - else - { - if (cec_global_info.my_node_index) - { - // prevent write operations - if (down_interruptible(&init_mutex)) + if (cec_global_info.my_node_index) { - printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); - return; - } - hdmitx_device->cec_init_ready = 0; - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - - amlogic_cec_log_dbg("start reset\n"); - // regain rx interrupts - cec_enable_irq(); - if (cec_rx_buf_check()) { - cec_rx_buf_clear(); - } - cec_hw_reset(); + // prevent write operations + if (down_interruptible(&init_mutex)) + { + printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); + return; + } + hdmitx_device->cec_init_ready = 0; + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + amlogic_cec_log_dbg("start reset\n"); - hdmitx_device->cec_init_ready = 1; + // regain rx interrupts + cec_enable_irq(); + if (cec_rx_buf_check()) { + cec_rx_buf_clear(); + } + cec_hw_reset(); - up(&init_mutex); - amlogic_cec_log_dbg("stop reset\n"); - } + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) - { - aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); - amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + hdmitx_device->cec_init_ready = 1; - if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) + up(&init_mutex); + amlogic_cec_log_dbg("stop reset\n"); + } + + if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) { - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - } - else + aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); + amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); + + if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) { - // let the libCEC ask for new physical Address - entry->buffer[0] = 0xff; - entry->size = 1; - INIT_LIST_HEAD(&entry->list); - - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - amlogic_cec_log_dbg("trigger libCEC\n"); - wake_up_interruptible(&cec_rx_struct.waitq); + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + } + else + { + // let the libCEC ask for new physical Address + entry->buffer[0] = 0xff; + entry->size = 1; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + amlogic_cec_log_dbg("trigger libCEC\n"); + wake_up_interruptible(&cec_rx_struct.waitq); + } } } } @@ -305,8 +308,6 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) { - unsigned long spin_flags; - struct cec_rx_list *entry; unsigned int tx_msg_state; unsigned int rx_msg_state; @@ -363,7 +364,7 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 cec_gpi_init(); #endif - + cec_node_init(hdmitx_device); #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 aml_set_reg32_bits(P_PERIPHS_PIN_MUX_1, 1, 25, 1); // Clear CEC Int. state and set CEC Int. mask From dd6730c57045af869bf0599b5e754b55306495e4 Mon Sep 17 00:00:00 2001 From: kszaq Date: Thu, 28 Apr 2016 15:35:38 +0200 Subject: [PATCH 17/21] amlogic_cec: init CEC only once It is not neccessary to initialize CEC more than once, with single initialization it runs OK with after multiple libCEC reopen cycles. Only enable/disable IRQ on driver open/release. --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 88 +++++++++------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 021d17f430d9..7e9f88bedf42 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -223,8 +223,25 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) unsigned long spin_flags; struct cec_rx_list *entry; - if (atomic_read(&hdmi_on)) + if (atomic_read(&hdmi_on) && (0 == hdmitx_device->cec_init_ready)) { +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 + cec_gpi_init(); + aml_set_reg32_bits(P_PERIPHS_PIN_MUX_1, 1, 25, 1); + // Clear CEC Int. state and set CEC Int. mask + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR) | (1 << 23)); // Clear the interrupt + aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) | (1 << 23)); // Enable the hdmi cec interrupt +#endif +#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 + // GPIOAO_12 + aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 0, 14, 1); // bit[14]: AO_PWM_C pinmux //0xc8100014 + aml_set_reg32_bits(P_AO_RTI_PULL_UP_REG, 1, 12, 1); // bit[12]: enable AO_12 internal pull-up //0xc810002c + aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 1, 17, 1); // bit[17]: AO_CEC pinmux //0xc8100014 + 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); +#endif cec_phy_addr = (((hdmitx_device->hdmi_info.vsdb_phy_addr.a) & 0xf) << 12) | (((hdmitx_device->hdmi_info.vsdb_phy_addr.b) & 0xf) << 8) | (((hdmitx_device->hdmi_info.vsdb_phy_addr.c) & 0xf) << 4) @@ -246,34 +263,6 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) } else { - if (cec_global_info.my_node_index) - { - // prevent write operations - if (down_interruptible(&init_mutex)) - { - printk(KERN_ERR "[amlogic] ##### cec node init interrupted! #####\n"); - return; - } - hdmitx_device->cec_init_ready = 0; - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - - amlogic_cec_log_dbg("start reset\n"); - - // regain rx interrupts - cec_enable_irq(); - if (cec_rx_buf_check()) { - cec_rx_buf_clear(); - } - cec_hw_reset(); - - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - hdmitx_device->cec_init_ready = 1; - - up(&init_mutex); - amlogic_cec_log_dbg("stop reset\n"); - } - if ((aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff) != cec_phy_addr) { aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); @@ -303,6 +292,8 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) } } } + + hdmitx_device->cec_init_ready = 1; } } @@ -352,6 +343,8 @@ static irqreturn_t amlogic_cec_irq_handler(int irq, void *dummy) static int amlogic_cec_open(struct inode *inode, struct file *file) { int ret = 0; + unsigned long spin_flags; + struct cec_rx_list* entry = NULL; if (atomic_read(&hdmi_on)) { @@ -361,32 +354,23 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) else { atomic_inc(&hdmi_on); -#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 - cec_gpi_init(); -#endif + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + while(!list_empty(&cec_rx_struct.list)) + { + entry = list_first_entry(&cec_rx_struct.list, struct cec_rx_list, list); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + cec_node_init(hdmitx_device); -#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 - aml_set_reg32_bits(P_PERIPHS_PIN_MUX_1, 1, 25, 1); - // Clear CEC Int. state and set CEC Int. mask - aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_STAT_CLR) | (1 << 23)); // Clear the interrupt - aml_write_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK, aml_read_reg32(P_SYS_CPU_0_IRQ_IN1_INTR_MASK) | (1 << 23)); // Enable the hdmi cec interrupt -#endif -#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 -// GPIOAO_12 - aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 0, 14, 1); // bit[14]: AO_PWM_C pinmux //0xc8100014 - aml_set_reg32_bits(P_AO_RTI_PULL_UP_REG, 1, 12, 1); // bit[12]: enable AO_12 internal pull-up //0xc810002c - aml_set_reg32_bits(P_AO_RTI_PIN_MUX_REG, 1, 17, 1); // bit[17]: AO_CEC pinmux //0xc8100014 - 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); -#endif - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); - hrtimer_start(&cec_late_timer, ktime_set(0, 384*1000*1000), HRTIMER_MODE_REL); + cec_enable_irq(); - hdmitx_device->cec_init_ready = 1; + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + hrtimer_start(&cec_late_timer, ktime_set(0, 384*1000*1000), HRTIMER_MODE_REL); } return ret; } @@ -395,6 +379,8 @@ static int amlogic_cec_release(struct inode *inode, struct file *file) { amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + cec_disable_irq(); + atomic_dec(&hdmi_on); return 0; From 80da95cd871015b0890a2f88f34113749af4870e Mon Sep 17 00:00:00 2001 From: kszaq Date: Thu, 5 May 2016 16:05:00 +0200 Subject: [PATCH 18/21] amlogic_cec: always trigger libCEC on first open --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 7e9f88bedf42..65fc7de9e7ac 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -268,7 +268,7 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); - if ((hdmitx_device->cec_init_ready != 0) && (hdmitx_device->hpd_state != 0)) + if (hdmitx_device->hpd_state != 0) { if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) { From 773d4df9ad529df40367b5d3444694dce5f43f94 Mon Sep 17 00:00:00 2001 From: kszaq Date: Wed, 11 May 2016 16:05:35 +0200 Subject: [PATCH 19/21] amlogic_cec: trigger libCEC on every driver open --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 48 +++++++++++----------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index 65fc7de9e7ac..c54cb4948b70 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -220,8 +220,6 @@ static enum hrtimer_restart cec_late_check_rx_buffer(struct hrtimer *timer) void cec_node_init(hdmitx_dev_t* hdmitx_device) { unsigned long cec_phy_addr; - unsigned long spin_flags; - struct cec_rx_list *entry; if (atomic_read(&hdmi_on) && (0 == hdmitx_device->cec_init_ready)) { @@ -267,29 +265,6 @@ void cec_node_init(hdmitx_dev_t* hdmitx_device) { aml_write_reg32(P_AO_DEBUG_REG1, (aml_read_reg32(P_AO_DEBUG_REG1) & (0xf << 16)) | cec_phy_addr); amlogic_cec_log_dbg("physical address:0x%x\n", aml_read_reg32(P_AO_DEBUG_REG1) & 0xffff); - - if (hdmitx_device->hpd_state != 0) - { - if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) - { - amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); - } - else - { - // let the libCEC ask for new physical Address - entry->buffer[0] = 0xff; - entry->size = 1; - INIT_LIST_HEAD(&entry->list); - - spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); - list_add_tail(&entry->list, &cec_rx_struct.list); - amlogic_cec_set_rx_state(STATE_DONE); - spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); - - amlogic_cec_log_dbg("trigger libCEC\n"); - wake_up_interruptible(&cec_rx_struct.waitq); - } - } } } @@ -370,6 +345,29 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + if (hdmitx_device->hpd_state != 0) + { + if ((entry = kmalloc(sizeof(struct cec_rx_list), GFP_ATOMIC)) == NULL) + { + amlogic_cec_log_dbg("can't alloc cec_rx_list\n"); + } + else + { + // let the libCEC ask for new physical Address + entry->buffer[0] = 0xff; + entry->size = 1; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + list_add_tail(&entry->list, &cec_rx_struct.list); + amlogic_cec_set_rx_state(STATE_DONE); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + amlogic_cec_log_dbg("trigger libCEC\n"); + wake_up_interruptible(&cec_rx_struct.waitq); + } + } + hrtimer_start(&cec_late_timer, ktime_set(0, 384*1000*1000), HRTIMER_MODE_REL); } return ret; From f41db309751e676bb7ddeb826b1fc68d0c4e2510 Mon Sep 17 00:00:00 2001 From: kszaq Date: Thu, 12 May 2016 20:07:52 +0200 Subject: [PATCH 20/21] amlogic_cec: always save logical address to cec_global_info This prevents setting different logical address in cec_hw_reset. --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index c54cb4948b70..b241bd850a03 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -191,6 +191,12 @@ static int amlogic_cec_read_hw() return retval; } +static void amlogic_cec_set_logical_addr(unsigned int logical_addr) +{ + amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | logical_addr); + cec_global_info.my_node_index = logical_addr; +} + unsigned short cec_log_addr_to_dev_type(unsigned char log_addr) { // unused, just to satisfy the linker @@ -343,7 +349,7 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) cec_enable_irq(); - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + amlogic_cec_set_logical_addr(0xf); if (hdmitx_device->hpd_state != 0) { @@ -375,7 +381,7 @@ static int amlogic_cec_open(struct inode *inode, struct file *file) static int amlogic_cec_release(struct inode *inode, struct file *file) { - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + amlogic_cec_set_logical_addr(0xf); cec_disable_irq(); @@ -501,8 +507,7 @@ static long amlogic_cec_ioctl(struct file *file, unsigned int cmd, return -EFAULT; } - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | logical_addr); - cec_global_info.my_node_index = logical_addr; + amlogic_cec_set_logical_addr(logical_addr); /* * use DEBUG_REG1 bit 16 ~ 31 to save logic address. * So uboot can use this logic address directly @@ -571,7 +576,7 @@ static int amlogic_cec_init(void) hdmitx_device = get_hdmitx_device(); amlogic_cec_log_dbg("CEC init\n"); - amlogic_cec_write_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf); + amlogic_cec_set_logical_addr(0xf); #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 hdmi_wr_reg(CEC0_BASE_ADDR+CEC_CLOCK_DIV_H, 0x00 ); From cbeeb32d7ba418cdf63a9240312708465213e82a Mon Sep 17 00:00:00 2001 From: kszaq Date: Tue, 24 May 2016 10:32:33 +0200 Subject: [PATCH 21/21] amlogic_cec: use common parameter to toggle debugging and turn it off by default This would also affect debug logging by methods in arch/arm/mach_meson* --- drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c | 29 ++++------------------ drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c | 17 ------------- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c index b241bd850a03..825dc77f0fd5 100644 --- a/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c +++ b/drivers/amlogic/hdmi/hdmi_tx/amlogic_cec.c @@ -39,19 +39,20 @@ #define CONFIG_TV_DEBUG // for verbose output //#undef CONFIG_TV_DEBUG -unsigned long amlogic_cec_debug_flag = 1; MODULE_AUTHOR("Gerald Dachs"); MODULE_DESCRIPTION("Amlogic CEC driver"); MODULE_LICENSE("GPL"); -//unused, only left to satisfy the linker -bool cec_msg_dbg_en = 1; +bool cec_msg_dbg_en = 0; + +MODULE_PARM_DESC(cec_msg_dbg_en, "\n cec_msg_dbg_en\n"); +module_param(cec_msg_dbg_en, bool, 0664); #define DRV_NAME "amlogic_cec" #ifndef amlogic_cec_log_dbg #define amlogic_cec_log_dbg(fmt, ...) \ - if (amlogic_cec_debug_flag) \ + if (cec_msg_dbg_en) \ printk(KERN_INFO "[%s] %s(): " fmt, DRV_NAME, __func__, ##__VA_ARGS__) #endif @@ -120,26 +121,6 @@ static void amlogic_cec_set_tx_state(enum cec_state state) atomic_set(&cec_tx_struct.state, state); } -static void amlogic_cec_msg_dump(char * msg_tag, const unsigned char *data, unsigned char count) -{ - int i; - int pos; - unsigned char msg_log_buf[128] = { 0 }; - - if (amlogic_cec_debug_flag == 1) - { - pos = 0; - pos += sprintf(msg_log_buf + pos, "msg %s len: %d dat: ", msg_tag, count); - for (i = 0; i < count; ++i) - { - pos += sprintf(msg_log_buf + pos, "%02x ", data[i]); - } - pos += sprintf(msg_log_buf + pos, "\n"); - msg_log_buf[pos] = '\0'; - hdmi_print(INF, "[amlogic_cec] dump: %s", msg_log_buf); - } -} - static unsigned int amlogic_cec_read_reg(unsigned int reg) { #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 diff --git a/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c b/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c index 2b11c72f22c3..c2a8d60ee97a 100755 --- a/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c +++ b/drivers/amlogic/hdmi/hdmi_tx/hdmi_tx.c @@ -590,19 +590,6 @@ static ssize_t show_cec_lang_config(struct device * dev, struct device_attribute pos+=snprintf(buf+pos, PAGE_SIZE, "%x\n",cec_global_info.cec_node_info[cec_global_info.my_node_index].menu_lang); return pos; } -#else - -extern unsigned long amlogic_cec_debug_flag; - -static ssize_t show_amlogic_cec_debug_config(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "amlogic_cec_debug:%lu\n", amlogic_cec_debug_flag); -} - -static ssize_t store_amlogic_cec_debug_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - return kstrtoul(buf, 16, &amlogic_cec_debug_flag) ? 0 : count; -} #endif /*aud_mode attr*/ @@ -983,8 +970,6 @@ static DEVICE_ATTR(cec, S_IWUSR | S_IRUGO, show_cec, store_cec); static DEVICE_ATTR(cec_config, S_IWUSR | S_IRUGO | S_IWGRP, show_cec_config, store_cec_config); //static DEVICE_ATTR(cec_config, S_IWUGO | S_IRUGO , NULL, store_cec_config); static DEVICE_ATTR(cec_lang_config, S_IWUSR | S_IRUGO | S_IWGRP, show_cec_lang_config, store_cec_lang_config); -#else -static DEVICE_ATTR(amlogic_cec_debug_config, S_IWUSR | S_IRUGO | S_IWGRP, show_amlogic_cec_debug_config, store_amlogic_cec_debug_config); #endif /***************************** @@ -1668,8 +1653,6 @@ static int amhdmitx_probe(struct platform_device *pdev) ret=device_create_file(hdmitx_dev, &dev_attr_cec); ret=device_create_file(hdmitx_dev, &dev_attr_cec_config); ret=device_create_file(hdmitx_dev, &dev_attr_cec_lang_config); -#else - ret=device_create_file(hdmitx_dev, &dev_attr_amlogic_cec_debug_config); #endif if (hdmitx_dev == NULL) {