diff options
author | Srikant Patnaik | 2015-01-13 15:08:24 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-13 15:08:24 +0530 |
commit | 97327692361306d1e6259021bc425e32832fdb50 (patch) | |
tree | fe9088f3248ec61e24f404f21b9793cb644b7f01 /drivers/video/wmt/dev-cec.c | |
parent | 2d05a8f663478a44e088d122e0d62109bbc801d0 (diff) | |
parent | a3a8b90b61e21be3dde9101c4e86c881e0f06210 (diff) | |
download | FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.tar.gz FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.tar.bz2 FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.zip |
dirty fix to merging
Diffstat (limited to 'drivers/video/wmt/dev-cec.c')
-rw-r--r-- | drivers/video/wmt/dev-cec.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/drivers/video/wmt/dev-cec.c b/drivers/video/wmt/dev-cec.c new file mode 100644 index 00000000..f03b202b --- /dev/null +++ b/drivers/video/wmt/dev-cec.c @@ -0,0 +1,488 @@ +/*++ + * linux/drivers/video/wmt/dev-cec.c + * WonderMedia video post processor (VPP) driver + * + * Copyright c 2013 WonderMedia Technologies, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#define DEV_CEC_C + +/* #define DEBUG */ +#include <linux/platform_device.h> +#include <linux/major.h> +#include <linux/cdev.h> +#include <linux/netlink.h> +#include <net/sock.h> +#include "cec.h" + +#define DRIVER_NAME "wmt-cec" + +#define TXNORSP 0x8000 /* Tx no response interrupt */ +#define TXLSARB 0x2000 /* Tx lose arbitration */ +#define TXDONE 0x0100 /* Tx done */ +#define RXFAIL 0x0002 /* Rx fail, details in Rx10 */ +#define RXDONE 0x0001 /* Rx done */ + +#define MAX_RETRY 3 +#define MAX_TIMEOUT 5000 + +static int tx_state; + +static struct cdev *wmt_cec_cdev; +static DEFINE_SEMAPHORE(wmt_cec_sem); + +struct wmt_cec_msg recv_msg; +static void wmt_cec_do_rx_notify(struct work_struct *ptr) +{ + vpp_netlink_notify(USER_PID, DEVICE_RX_DATA, (int)&recv_msg); +} +DECLARE_DELAYED_WORK(wmt_cec_rx_work, wmt_cec_do_rx_notify); + + +/* receive message interrupt handling */ +static void wmt_cec_do_recv(void) +{ + memset(&recv_msg, 0, sizeof(recv_msg)); + /* get received data */ + recv_msg.msglen = wmt_cec_rx_data(recv_msg.msgdata); + if (recv_msg.msglen > MAX_MSG_BYTE) + recv_msg.msglen = MAX_MSG_BYTE; + + DBGMSG("read a received byte! msglen: %d\n", recv_msg.msglen); + + /* clear receive blockage */ + + /* notify AP the received message, let AP decide what to response */ + schedule_delayed_work(&wmt_cec_rx_work, HZ / 10); +} + +/* check if logic address is valid */ +static int bvalidaddr(char addr) +{ + if (addr > 15) + return 0; + return 1; +} + +/* make sure cec line is not busy */ +static int tx_get_cecline(void) +{ + int timeout = 400; + + while (timeout-- > 0) { + /* if not busy */ + if (1) + return 0; + msleep(1); + } + return -ETIMEDOUT; +} + +/* transfer a time*/ +DECLARE_COMPLETION(txcomp); +static int wmt_cec_do_xfer_one(char *msgdata, int msglen) +{ + int ret; + unsigned long jiffies; + + ret = tx_get_cecline(); + if (ret != 0) + ret = -EAGAIN; + + wmt_cec_tx_data(msgdata, msglen); + + jiffies = msecs_to_jiffies((unsigned int)MAX_TIMEOUT); + jiffies = wait_for_completion_timeout(&txcomp, jiffies); + if (jiffies == 0) + return -EAGAIN; + + if (tx_state == TXLSARB) + return -EAGAIN; + + return tx_state; +} + +/* transfer a message, including retransmission if needed*/ +static int wmt_cec_do_xfer(char *msgdata, int msglen) +{ + int retry; + int ret; + char srcaddr, tgtaddr; + + if (msglen < 1) { + DPRINT("[CEC] xfer: invalid message, msglen is less than 1.\n"); + return -1; + } + + srcaddr = (msgdata[0] & 0xf0) >> 4; + tgtaddr = (msgdata[0] & 0x0f); + if (!bvalidaddr(srcaddr) || !bvalidaddr(tgtaddr)) { + DPRINT("[CEC] xfer: invalid logic address in msg data.\n"); + return -1; + } + + for (retry = 0; retry < MAX_RETRY; retry++) { + ret = wmt_cec_do_xfer_one(msgdata, msglen); + if (ret != -EAGAIN) + goto out; + DPRINT("[CEC] Retrying transmission (%d)\n", retry); + udelay(100); + } + return -EAGAIN; +out: + /* if polling message ret is no-response, set logical address register, + and enable slave mode */ + if (srcaddr == tgtaddr && msglen == 1) { + if (ret == TXNORSP) { + DBGMSG("[CEC] logic addr register is 0x%x\n", tgtaddr); + return 0; + } else + return -1; + } else if (ret == TXDONE) + return 0; + else + return -1; +} + +/* irq handling function */ +irqreturn_t wmt_cec_do_irq(int irq, void *dev_id) +{ + int sts; + + sts = wmt_cec_get_int(); + wmt_cec_clr_int(sts); + if (sts & BIT0) { /* tx done */ + DBGMSG("[CEC] write ok int\n"); + complete(&txcomp); + tx_state = TXDONE; + } + if (sts & BIT1) { /* rx done */ + DBGMSG("[CEC] read ok int\n"); + wmt_cec_do_recv(); + } + if (sts & BIT2) { /* rx error */ + DBGMSG("[CEC] read error int\n"); + } + if (sts & BIT3) { /* tx arb fail */ + DBGMSG("[CEC] wr arb fail int\n"); + complete(&txcomp); + tx_state = TXLSARB; + } + if (sts & BIT4) { /* tx no ack */ +/* DBGMSG("[CEC] write no ack int(addr not match)\n"); */ + complete(&txcomp); + tx_state = TXNORSP; + } + return IRQ_HANDLED; +} + +static int wmt_cec_open( + struct inode *inode, + struct file *filp +) +{ + int ret = 0; + + DBGMSG("[CEC] wmt_cec_open\n"); + + down(&wmt_cec_sem); + wmt_cec_rx_enable(1); + up(&wmt_cec_sem); + return ret; +} /* End of videodecoder_open() */ + +static int wmt_cec_release( + struct inode *inode, + struct file *filp +) +{ + int ret = 0; + + DBGMSG("[CEC] wmt_cec_release\n"); + + down(&wmt_cec_sem); + wmt_cec_rx_enable(0); + up(&wmt_cec_sem); + return ret; +} /* End of videodecoder_release() */ + +static long wmt_cec_ioctl( + struct file *filp, + unsigned int cmd, + unsigned long arg +) +{ + int ret = -EINVAL; + + DBGMSG("[CEC] wmt_cec_ioctl 0x%x,0x%x\n", cmd, arg); + + /* check type and number, if fail return ENOTTY */ + if (_IOC_TYPE(cmd) != WMT_CEC_IOC_MAGIC) + return -ENOTTY; + if (_IOC_NR(cmd) > WMT_CEC_IOC_MAXNR) + return -ENOTTY; + + /* check argument area */ + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, + (void __user *) arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, + (void __user *) arg, _IOC_SIZE(cmd)); + else + ret = 0; + + if (ret) + return -EFAULT; + + down(&wmt_cec_sem); + switch (cmd) { + case CECIO_TX_DATA: + { + struct wmt_cec_msg msg; + + ret = copy_from_user((void *) &msg, (const void *)arg, + sizeof(struct wmt_cec_msg)); + wmt_cec_do_xfer(msg.msgdata, msg.msglen); + } + break; + case CECIO_TX_LOGADDR: + wmt_cec_set_logical_addr((arg & 0xFF00) >> 8, arg & 0xFF, 1); + break; + case CECIO_RX_PHYADDR: + { + wmt_phy_addr_t parm; + + parm.phy_addr = edid_get_hdmi_phy_addr(); + ret = copy_to_user((void *)arg, (void *)&parm, + sizeof(wmt_phy_addr_t)); + } + break; + default: + DBGMSG("[CEC] *W* wmt_cec_ioctl cmd 0x%x\n", cmd); + break; + } + up(&wmt_cec_sem); + return ret; +} /* End of videodecoder_ioctl() */ + +static int wmt_cec_mmap( + struct file *filp, + struct vm_area_struct *vma +) +{ + int ret = -EINVAL; + down(&wmt_cec_sem); + up(&wmt_cec_sem); + return ret; +} + +static ssize_t wmt_cec_read( + struct file *filp, + char __user *buf, + size_t count, + loff_t *f_pos +) +{ + ssize_t ret = 0; + down(&wmt_cec_sem); + up(&wmt_cec_sem); + return ret; +} /* videodecoder_read */ + +static ssize_t wmt_cec_write( + struct file *filp, + const char __user *buf, + size_t count, + loff_t *f_pos +) +{ + ssize_t ret = 0; + down(&wmt_cec_sem); + up(&wmt_cec_sem); + return ret; +} /* End of videodecoder_write() */ + +struct file_operations wmt_cec_fops = { + .owner = THIS_MODULE, + .open = wmt_cec_open, + .release = wmt_cec_release, + .read = wmt_cec_read, + .write = wmt_cec_write, + .unlocked_ioctl = wmt_cec_ioctl, + .mmap = wmt_cec_mmap, +}; + +static int __init wmt_cec_probe +( + struct platform_device *dev /*!<; // a pointer to struct device */ +) +{ + dev_t dev_no; + int ret; + + DBGMSG("[CEC] Enter wmt_cec_probe\n"); + + wmt_cec_init_hw(); + wmt_cec_set_logical_addr(4, 0xf, 1); /* default set boardcast address */ + wmt_cec_rx_enable(0); + /* wmt_cec_enable_loopback(1); */ + + dev_no = MKDEV(CEC_MAJOR, 0); + ret = register_chrdev_region(dev_no, 1, "wmtcec"); + if (ret < 0) { + DPRINT("can't get %s dev major %d\n", DRIVER_NAME, CEC_MAJOR); + return ret; + } + + /* register char device */ + wmt_cec_cdev = cdev_alloc(); + if (!wmt_cec_cdev) { + DPRINT("alloc dev error.\n"); + return -ENOMEM; + } + + cdev_init(wmt_cec_cdev, &wmt_cec_fops); + ret = cdev_add(wmt_cec_cdev, dev_no, 1); + if (ret) { + DPRINT("reg char dev error(%d).\n", ret); + cdev_del(wmt_cec_cdev); + return ret; + } + + if (vpp_request_irq(IRQ_VPP_IRQ20, wmt_cec_do_irq, + SA_INTERRUPT, "cec", (void *) 0)) + DPRINT("*E* request CEC ISR fail\n"); + vppif_reg32_out(REG_CEC_INT_ENABLE, 0x1f); + + DBGMSG("[CEC] Exit wmt_cec_probe(0x%x)\n", dev_no); + return 0; +} /* End of wmt_cec_probe */ + +static int wmt_cec_remove +( + struct platform_device *dev /*!<; // a pointer point to struct device */ +) +{ + return 0; +} /* End of wmt_cec_remove */ + +#ifdef CONFIG_PM +static int wmt_cec_suspend +( + struct platform_device *pDev, /*!<; // a pointer to struct device */ + pm_message_t state /*!<; // suspend state */ +) +{ + DPRINT("Enter wmt_cec_suspend\n"); + wmt_cec_do_suspend(); + return 0; +} /* End of wmt_cec_suspend */ + +static int wmt_cec_resume +( + struct platform_device *pDev /*!<; // a pointer to struct device */ +) +{ + DPRINT("Enter wmt_cec_resume\n"); + wmt_cec_do_resume(); + return 0; +} /* End of wmt_cec_resume */ +#else +#define wmt_cec_suspend NULL +#define wmt_cec_resume NULL +#endif + +/*************************************************************************** + device driver struct define +****************************************************************************/ +static struct platform_driver wmt_cec_driver = { + .driver.name = "wmtcec", /* equal to platform device name. */ + .driver.bus = &platform_bus_type, + .probe = wmt_cec_probe, + .remove = wmt_cec_remove, + .suspend = wmt_cec_suspend, + .resume = wmt_cec_resume, +}; + +/*************************************************************************** + platform device struct define +****************************************************************************/ +static u64 wmt_cec_dma_mask = 0xffffffffUL; +static struct platform_device wmt_cec_device = { + .name = "wmtcec", + .dev = { + .dma_mask = &wmt_cec_dma_mask, + .coherent_dma_mask = ~0, + }, +#if 0 + .id = 0, + .dev = { + .release = wmt_cec_platform_release, + }, + .num_resources = 0, /* ARRAY_SIZE(wmt_cec_resources), */ + .resource = NULL, /* wmt_cec_resources, */ +#endif +}; + +static int __init wmt_cec_init(void) +{ + int ret; + char buf[100]; + int varlen = 100; + unsigned int cec_enable = 0; + + if (wmt_getsyspara("wmt.display.cec", buf, &varlen) == 0) + vpp_parse_param(buf, &cec_enable, 1, 0); + + if (cec_enable == 0) + return 0; + + DBGMSG(KERN_ALERT "Enter wmt_cec_init\n"); + + ret = platform_driver_register(&wmt_cec_driver); + if (!ret) { + ret = platform_device_register(&wmt_cec_device); + if (ret) + platform_driver_unregister(&wmt_cec_driver); + } + return ret; +} + +static void __exit wmt_cec_exit(void) +{ + dev_t dev_no; + + DBGMSG(KERN_ALERT "Enter wmt_cec_exit\n"); + + platform_driver_unregister(&wmt_cec_driver); + platform_device_unregister(&wmt_cec_device); + dev_no = MKDEV(CEC_MAJOR, 0); + unregister_chrdev_region(dev_no, 1); + return; +} + +module_init(wmt_cec_init); +module_exit(wmt_cec_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("WMT CEC driver"); +MODULE_AUTHOR("WMT TECH"); +MODULE_VERSION("1.0.0"); + |