| Index: drivers/media/video/samsung/tv20/cec_s5pv210.c
|
| diff --git a/drivers/media/video/samsung/tv20/cec_s5pv210.c b/drivers/media/video/samsung/tv20/cec_s5pv210.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c5b6455b8accfc7a0810ad4bad247d36c6206517
|
| --- /dev/null
|
| +++ b/drivers/media/video/samsung/tv20/cec_s5pv210.c
|
| @@ -0,0 +1,279 @@
|
| +/* linux/drivers/media/video/samsung/tv20/cec_s5pv210.c
|
| +*
|
| +* Copyright (c) 2010 Samsung Electronics Co., Ltd.
|
| +* http://www.samsung.com/
|
| +*
|
| +* S5PV210 - cec ftn file for Samsung TVOut driver
|
| +*
|
| +* 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 <linux/module.h>
|
| +#include <linux/kernel.h>
|
| +
|
| +#include <linux/io.h>
|
| +
|
| +#include <mach/map.h>
|
| +#include <mach/regs-clock.h>
|
| +#include <mach/regs-cec.h>
|
| +#include "cec.h"
|
| +
|
| +#ifdef CECDEBUG
|
| +#define CECPRINTK(fmt, args...) \
|
| + printk(KERN_INFO "\t\t[CEC] %s: " fmt, __func__ , ## args)
|
| +#else
|
| +#define CECPRINTK(fmt, args...)
|
| +#endif
|
| +
|
| +static struct resource *cec_mem;
|
| +void __iomem *cec_base;
|
| +
|
| +#define S5P_HDMI_FIN 24000000
|
| +#define CEC_DIV_RATIO 187500
|
| +
|
| +void tv_cec_set_divider(void)
|
| +{
|
| + u32 div_ratio, reg, div_val;
|
| +
|
| + div_ratio = S5P_HDMI_FIN/CEC_DIV_RATIO - 1;
|
| +
|
| + reg = readl(S5P_HDMI_PHY_CONTROL);
|
| + reg = (reg & ~(0x3FF<<16)) | (div_ratio << 16);
|
| +
|
| + writel(reg, S5P_HDMI_PHY_CONTROL);
|
| +
|
| + div_val = CEC_DIV_RATIO * 0.00005 - 1;
|
| +
|
| + writeb(0x0, cec_base + CEC_DIVISOR_3);
|
| + writeb(0x0, cec_base + CEC_DIVISOR_2);
|
| + writeb(0x0, cec_base + CEC_DIVISOR_1);
|
| + writeb(div_val, cec_base + CEC_DIVISOR_0);
|
| +
|
| + CECPRINTK("CEC_DIVISOR_3 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_3));
|
| + CECPRINTK("CEC_DIVISOR_2 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_2));
|
| + CECPRINTK("CEC_DIVISOR_1 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_1));
|
| + CECPRINTK("CEC_DIVISOR_0 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_0));
|
| +}
|
| +
|
| +void tv_cec_enable_rx(void)
|
| +{
|
| + u8 reg;
|
| + reg = readb(cec_base + CEC_RX_CTRL);
|
| + reg |= CEC_RX_CTRL_ENABLE;
|
| + writeb(reg, cec_base + CEC_RX_CTRL);
|
| +
|
| + CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL));
|
| +}
|
| +
|
| +void tv_cec_mask_rx_interrupts(void)
|
| +{
|
| + u8 reg;
|
| + reg = readb(cec_base + CEC_IRQ_MASK);
|
| + reg |= CEC_IRQ_RX_DONE;
|
| + reg |= CEC_IRQ_RX_ERROR;
|
| + writeb(reg, cec_base + CEC_IRQ_MASK);
|
| +
|
| + CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK));
|
| +}
|
| +
|
| +void tv_cec_unmask_rx_interrupts(void)
|
| +{
|
| + u8 reg;
|
| + reg = readb(cec_base + CEC_IRQ_MASK);
|
| + reg &= ~CEC_IRQ_RX_DONE;
|
| + reg &= ~CEC_IRQ_RX_ERROR;
|
| + writeb(reg, cec_base + CEC_IRQ_MASK);
|
| +
|
| + CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK));
|
| +}
|
| +
|
| +void tv_cec_mask_tx_interrupts(void)
|
| +{
|
| + u8 reg;
|
| + reg = readb(cec_base + CEC_IRQ_MASK);
|
| + reg |= CEC_IRQ_TX_DONE;
|
| + reg |= CEC_IRQ_TX_ERROR;
|
| + writeb(reg, cec_base + CEC_IRQ_MASK);
|
| +
|
| + CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK));
|
| +
|
| +}
|
| +
|
| +void tv_cec_unmask_tx_interrupts(void)
|
| +{
|
| + u8 reg;
|
| + reg = readb(cec_base + CEC_IRQ_MASK);
|
| + reg &= ~CEC_IRQ_TX_DONE;
|
| + reg &= ~CEC_IRQ_TX_ERROR;
|
| + writeb(reg, cec_base + CEC_IRQ_MASK);
|
| +
|
| + CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK));
|
| +}
|
| +
|
| +void tv_cec_reset(void)
|
| +{
|
| + writeb(CEC_RX_CTRL_RESET, cec_base + CEC_RX_CTRL); /* reset CEC Rx */
|
| + writeb(CEC_TX_CTRL_RESET, cec_base + CEC_TX_CTRL); /* reset CEC Tx */
|
| +
|
| + CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL));
|
| + CECPRINTK("CEC_TX_CTRL = 0x%08x \n", readb(cec_base + CEC_TX_CTRL));
|
| +}
|
| +
|
| +void tv_cec_tx_reset(void)
|
| +{
|
| + writeb(CEC_TX_CTRL_RESET, cec_base + CEC_TX_CTRL); /* reset CEC Tx */
|
| +
|
| + CECPRINTK("CEC_TX_CTRL = 0x%08x \n", readb(cec_base + CEC_TX_CTRL));
|
| +}
|
| +
|
| +void tv_cec_rx_reset(void)
|
| +{
|
| + writeb(CEC_RX_CTRL_RESET, cec_base + CEC_RX_CTRL); /* reset CEC Rx */
|
| + CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL));
|
| +}
|
| +
|
| +void tv_cec_threshold(void)
|
| +{
|
| + writeb(CEC_FILTER_THRESHOLD, cec_base + CEC_RX_FILTER_TH);
|
| + /* setup filter */
|
| + writeb(0, cec_base + CEC_RX_FILTER_CTRL);
|
| + CECPRINTK("CEC_RX_FILTER_TH = 0x%08x \n",
|
| + readb(cec_base + CEC_RX_FILTER_TH));
|
| +}
|
| +
|
| +void tv_cec_copy_packet(char *data, size_t count)
|
| +{
|
| + int i = 0;
|
| + u8 reg;
|
| + /* copy packet to hardware buffer */
|
| + while (i < count) {
|
| + writeb(data[i], cec_base + (CEC_TX_BUFF0 + (i*4)));
|
| + i++;
|
| + }
|
| +
|
| + /* set number of bytes to transfer */
|
| + writeb(count, cec_base + CEC_TX_BYTES);
|
| +
|
| + tv_cec_set_tx_state(STATE_TX);
|
| +
|
| + /* start transfer */
|
| + reg = readb(cec_base + CEC_TX_CTRL);
|
| +
|
| + reg |= CEC_TX_CTRL_START;
|
| +
|
| + /* if message is broadcast message - set corresponding bit */
|
| + if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST)
|
| + reg |= CEC_TX_CTRL_BCAST;
|
| + else
|
| + reg &= ~CEC_TX_CTRL_BCAST;
|
| +
|
| + /* set number of retransmissions */
|
| + reg |= 0x50;
|
| +
|
| + writeb(reg, cec_base + CEC_TX_CTRL);
|
| +}
|
| +
|
| +void tv_cec_set_addr(u32 addr)
|
| +{
|
| + writeb(addr & 0x0F, cec_base + CEC_LOGIC_ADDR);
|
| +
|
| +}
|
| +
|
| +u32 tv_cec_get_status(void)
|
| +{
|
| + u32 status = 0;
|
| +
|
| + status = readb(cec_base + CEC_STATUS_0);
|
| + status |= readb(cec_base + CEC_STATUS_1) << 8;
|
| + status |= readb(cec_base + CEC_STATUS_2) << 16;
|
| + status |= readb(cec_base + CEC_STATUS_3) << 24;
|
| +
|
| + CECPRINTK("status = 0x%x!\n", status);
|
| +
|
| + return status;
|
| +}
|
| +
|
| +void tv_clr_pending_tx(void)
|
| +{
|
| + /* clear interrupt pending bit */
|
| + writeb(CEC_IRQ_TX_DONE | CEC_IRQ_TX_ERROR, cec_base + CEC_IRQ_CLEAR);
|
| +}
|
| +
|
| +void tv_clr_pending_rx(void)
|
| +{
|
| + /* clear interrupt pending bit */
|
| + writeb(CEC_IRQ_RX_DONE | CEC_IRQ_RX_ERROR, cec_base + CEC_IRQ_CLEAR);
|
| +}
|
| +
|
| +void tv_cec_get_rx_buf(u32 size, u8 *buffer)
|
| +{
|
| + u32 i = 0;
|
| +
|
| + while (i < size) {
|
| + buffer[i] = readb(cec_base + CEC_RX_BUFF0 + (i * 4));
|
| + i++;
|
| + }
|
| +}
|
| +
|
| +void __init tv_cec_probe(struct platform_device *pdev)
|
| +{
|
| + struct resource *res;
|
| + size_t size;
|
| +
|
| + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
| +
|
| + if (res == NULL) {
|
| + dev_err(&pdev->dev,
|
| + "failed to get memory region resource for cec\n");
|
| + goto cec_err;
|
| +
|
| + }
|
| +
|
| + size = (res->end - res->start) + 1;
|
| +
|
| + cec_mem = request_mem_region(res->start, size, pdev->name);
|
| +
|
| + if (cec_mem == NULL) {
|
| + dev_err(&pdev->dev,
|
| + "failed to get memory region for cec\n");
|
| + goto cec_err;
|
| +
|
| + }
|
| +
|
| + cec_base = ioremap(res->start, size);
|
| +
|
| + if (cec_base == NULL) {
|
| + dev_err(&pdev->dev,
|
| + "failed to ioremap address region for cec\n");
|
| + goto cec_err;
|
| +
|
| +
|
| + }
|
| +
|
| + return;
|
| +
|
| +cec_err:
|
| + dev_err(&pdev->dev,
|
| + "failed to cec_probe()\n");
|
| +
|
| +}
|
| +
|
| +int __init tv_cec_release(struct platform_device *pdev)
|
| +{
|
| + iounmap(cec_base);
|
| +
|
| + /* remove memory region */
|
| + if (cec_mem != NULL) {
|
| + if (release_resource(cec_mem))
|
| + dev_err(&pdev->dev,
|
| + "Can't remove tvout drv !!\n");
|
| +
|
| + kfree(cec_mem);
|
| +
|
| + cec_mem = NULL;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
|
|