OLD | NEW |
(Empty) | |
| 1 /* linux/drivers/media/video/samsung/tv20/cec_s5pv210.c |
| 2 * |
| 3 * Copyright (c) 2010 Samsung Electronics Co., Ltd. |
| 4 * http://www.samsung.com/ |
| 5 * |
| 6 * S5PV210 - cec ftn file for Samsung TVOut driver |
| 7 * |
| 8 * This program is free software; you can redistribute it and/or modify |
| 9 * it under the terms of the GNU General Public License version 2 as |
| 10 * published by the Free Software Foundation. |
| 11 */ |
| 12 |
| 13 #include <linux/module.h> |
| 14 #include <linux/kernel.h> |
| 15 |
| 16 #include <linux/io.h> |
| 17 |
| 18 #include <mach/map.h> |
| 19 #include <mach/regs-clock.h> |
| 20 #include <mach/regs-cec.h> |
| 21 #include "cec.h" |
| 22 |
| 23 #ifdef CECDEBUG |
| 24 #define CECPRINTK(fmt, args...) \ |
| 25 printk(KERN_INFO "\t\t[CEC] %s: " fmt, __func__ , ## args) |
| 26 #else |
| 27 #define CECPRINTK(fmt, args...) |
| 28 #endif |
| 29 |
| 30 static struct resource *cec_mem; |
| 31 void __iomem *cec_base; |
| 32 |
| 33 #define S5P_HDMI_FIN 24000000 |
| 34 #define CEC_DIV_RATIO 187500 |
| 35 |
| 36 void tv_cec_set_divider(void) |
| 37 { |
| 38 u32 div_ratio, reg, div_val; |
| 39 |
| 40 div_ratio = S5P_HDMI_FIN/CEC_DIV_RATIO - 1; |
| 41 |
| 42 reg = readl(S5P_HDMI_PHY_CONTROL); |
| 43 reg = (reg & ~(0x3FF<<16)) | (div_ratio << 16); |
| 44 |
| 45 writel(reg, S5P_HDMI_PHY_CONTROL); |
| 46 |
| 47 div_val = CEC_DIV_RATIO * 0.00005 - 1; |
| 48 |
| 49 writeb(0x0, cec_base + CEC_DIVISOR_3); |
| 50 writeb(0x0, cec_base + CEC_DIVISOR_2); |
| 51 writeb(0x0, cec_base + CEC_DIVISOR_1); |
| 52 writeb(div_val, cec_base + CEC_DIVISOR_0); |
| 53 |
| 54 CECPRINTK("CEC_DIVISOR_3 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_3)); |
| 55 CECPRINTK("CEC_DIVISOR_2 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_2)); |
| 56 CECPRINTK("CEC_DIVISOR_1 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_1)); |
| 57 CECPRINTK("CEC_DIVISOR_0 = 0x%08x\n", readb(cec_base + CEC_DIVISOR_0)); |
| 58 } |
| 59 |
| 60 void tv_cec_enable_rx(void) |
| 61 { |
| 62 u8 reg; |
| 63 reg = readb(cec_base + CEC_RX_CTRL); |
| 64 reg |= CEC_RX_CTRL_ENABLE; |
| 65 writeb(reg, cec_base + CEC_RX_CTRL); |
| 66 |
| 67 CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL)); |
| 68 } |
| 69 |
| 70 void tv_cec_mask_rx_interrupts(void) |
| 71 { |
| 72 u8 reg; |
| 73 reg = readb(cec_base + CEC_IRQ_MASK); |
| 74 reg |= CEC_IRQ_RX_DONE; |
| 75 reg |= CEC_IRQ_RX_ERROR; |
| 76 writeb(reg, cec_base + CEC_IRQ_MASK); |
| 77 |
| 78 CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK)); |
| 79 } |
| 80 |
| 81 void tv_cec_unmask_rx_interrupts(void) |
| 82 { |
| 83 u8 reg; |
| 84 reg = readb(cec_base + CEC_IRQ_MASK); |
| 85 reg &= ~CEC_IRQ_RX_DONE; |
| 86 reg &= ~CEC_IRQ_RX_ERROR; |
| 87 writeb(reg, cec_base + CEC_IRQ_MASK); |
| 88 |
| 89 CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK)); |
| 90 } |
| 91 |
| 92 void tv_cec_mask_tx_interrupts(void) |
| 93 { |
| 94 u8 reg; |
| 95 reg = readb(cec_base + CEC_IRQ_MASK); |
| 96 reg |= CEC_IRQ_TX_DONE; |
| 97 reg |= CEC_IRQ_TX_ERROR; |
| 98 writeb(reg, cec_base + CEC_IRQ_MASK); |
| 99 |
| 100 CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK)); |
| 101 |
| 102 } |
| 103 |
| 104 void tv_cec_unmask_tx_interrupts(void) |
| 105 { |
| 106 u8 reg; |
| 107 reg = readb(cec_base + CEC_IRQ_MASK); |
| 108 reg &= ~CEC_IRQ_TX_DONE; |
| 109 reg &= ~CEC_IRQ_TX_ERROR; |
| 110 writeb(reg, cec_base + CEC_IRQ_MASK); |
| 111 |
| 112 CECPRINTK("CEC_IRQ_MASK = 0x%08x \n", readb(cec_base + CEC_IRQ_MASK)); |
| 113 } |
| 114 |
| 115 void tv_cec_reset(void) |
| 116 { |
| 117 writeb(CEC_RX_CTRL_RESET, cec_base + CEC_RX_CTRL); /* reset CEC Rx */ |
| 118 writeb(CEC_TX_CTRL_RESET, cec_base + CEC_TX_CTRL); /* reset CEC Tx */ |
| 119 |
| 120 CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL)); |
| 121 CECPRINTK("CEC_TX_CTRL = 0x%08x \n", readb(cec_base + CEC_TX_CTRL)); |
| 122 } |
| 123 |
| 124 void tv_cec_tx_reset(void) |
| 125 { |
| 126 writeb(CEC_TX_CTRL_RESET, cec_base + CEC_TX_CTRL); /* reset CEC Tx */ |
| 127 |
| 128 CECPRINTK("CEC_TX_CTRL = 0x%08x \n", readb(cec_base + CEC_TX_CTRL)); |
| 129 } |
| 130 |
| 131 void tv_cec_rx_reset(void) |
| 132 { |
| 133 writeb(CEC_RX_CTRL_RESET, cec_base + CEC_RX_CTRL); /* reset CEC Rx */ |
| 134 CECPRINTK("CEC_RX_CTRL = 0x%08x \n", readb(cec_base + CEC_RX_CTRL)); |
| 135 } |
| 136 |
| 137 void tv_cec_threshold(void) |
| 138 { |
| 139 writeb(CEC_FILTER_THRESHOLD, cec_base + CEC_RX_FILTER_TH); |
| 140 /* setup filter */ |
| 141 writeb(0, cec_base + CEC_RX_FILTER_CTRL); |
| 142 CECPRINTK("CEC_RX_FILTER_TH = 0x%08x \n", |
| 143 readb(cec_base + CEC_RX_FILTER_TH)); |
| 144 } |
| 145 |
| 146 void tv_cec_copy_packet(char *data, size_t count) |
| 147 { |
| 148 int i = 0; |
| 149 u8 reg; |
| 150 /* copy packet to hardware buffer */ |
| 151 while (i < count) { |
| 152 writeb(data[i], cec_base + (CEC_TX_BUFF0 + (i*4))); |
| 153 i++; |
| 154 } |
| 155 |
| 156 /* set number of bytes to transfer */ |
| 157 writeb(count, cec_base + CEC_TX_BYTES); |
| 158 |
| 159 tv_cec_set_tx_state(STATE_TX); |
| 160 |
| 161 /* start transfer */ |
| 162 reg = readb(cec_base + CEC_TX_CTRL); |
| 163 |
| 164 reg |= CEC_TX_CTRL_START; |
| 165 |
| 166 /* if message is broadcast message - set corresponding bit */ |
| 167 if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) |
| 168 reg |= CEC_TX_CTRL_BCAST; |
| 169 else |
| 170 reg &= ~CEC_TX_CTRL_BCAST; |
| 171 |
| 172 /* set number of retransmissions */ |
| 173 reg |= 0x50; |
| 174 |
| 175 writeb(reg, cec_base + CEC_TX_CTRL); |
| 176 } |
| 177 |
| 178 void tv_cec_set_addr(u32 addr) |
| 179 { |
| 180 writeb(addr & 0x0F, cec_base + CEC_LOGIC_ADDR); |
| 181 |
| 182 } |
| 183 |
| 184 u32 tv_cec_get_status(void) |
| 185 { |
| 186 u32 status = 0; |
| 187 |
| 188 status = readb(cec_base + CEC_STATUS_0); |
| 189 status |= readb(cec_base + CEC_STATUS_1) << 8; |
| 190 status |= readb(cec_base + CEC_STATUS_2) << 16; |
| 191 status |= readb(cec_base + CEC_STATUS_3) << 24; |
| 192 |
| 193 CECPRINTK("status = 0x%x!\n", status); |
| 194 |
| 195 return status; |
| 196 } |
| 197 |
| 198 void tv_clr_pending_tx(void) |
| 199 { |
| 200 /* clear interrupt pending bit */ |
| 201 writeb(CEC_IRQ_TX_DONE | CEC_IRQ_TX_ERROR, cec_base + CEC_IRQ_CLEAR); |
| 202 } |
| 203 |
| 204 void tv_clr_pending_rx(void) |
| 205 { |
| 206 /* clear interrupt pending bit */ |
| 207 writeb(CEC_IRQ_RX_DONE | CEC_IRQ_RX_ERROR, cec_base + CEC_IRQ_CLEAR); |
| 208 } |
| 209 |
| 210 void tv_cec_get_rx_buf(u32 size, u8 *buffer) |
| 211 { |
| 212 u32 i = 0; |
| 213 |
| 214 while (i < size) { |
| 215 buffer[i] = readb(cec_base + CEC_RX_BUFF0 + (i * 4)); |
| 216 i++; |
| 217 } |
| 218 } |
| 219 |
| 220 void __init tv_cec_probe(struct platform_device *pdev) |
| 221 { |
| 222 struct resource *res; |
| 223 size_t size; |
| 224 |
| 225 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 226 |
| 227 if (res == NULL) { |
| 228 dev_err(&pdev->dev, |
| 229 "failed to get memory region resource for cec\n"); |
| 230 goto cec_err; |
| 231 |
| 232 } |
| 233 |
| 234 size = (res->end - res->start) + 1; |
| 235 |
| 236 cec_mem = request_mem_region(res->start, size, pdev->name); |
| 237 |
| 238 if (cec_mem == NULL) { |
| 239 dev_err(&pdev->dev, |
| 240 "failed to get memory region for cec\n"); |
| 241 goto cec_err; |
| 242 |
| 243 } |
| 244 |
| 245 cec_base = ioremap(res->start, size); |
| 246 |
| 247 if (cec_base == NULL) { |
| 248 dev_err(&pdev->dev, |
| 249 "failed to ioremap address region for cec\n"); |
| 250 goto cec_err; |
| 251 |
| 252 |
| 253 } |
| 254 |
| 255 return; |
| 256 |
| 257 cec_err: |
| 258 dev_err(&pdev->dev, |
| 259 "failed to cec_probe()\n"); |
| 260 |
| 261 } |
| 262 |
| 263 int __init tv_cec_release(struct platform_device *pdev) |
| 264 { |
| 265 iounmap(cec_base); |
| 266 |
| 267 /* remove memory region */ |
| 268 if (cec_mem != NULL) { |
| 269 if (release_resource(cec_mem)) |
| 270 dev_err(&pdev->dev, |
| 271 "Can't remove tvout drv !!\n"); |
| 272 |
| 273 kfree(cec_mem); |
| 274 |
| 275 cec_mem = NULL; |
| 276 } |
| 277 |
| 278 return 0; |
| 279 } |
OLD | NEW |