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; |
+} |