0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/io.h>
0011 #include <linux/device.h>
0012
0013 #include "exynos_hdmi_cec.h"
0014 #include "regs-cec.h"
0015
0016 #define S5P_HDMI_FIN 24000000
0017 #define CEC_DIV_RATIO 320000
0018
0019 #define CEC_MESSAGE_BROADCAST_MASK 0x0F
0020 #define CEC_MESSAGE_BROADCAST 0x0F
0021 #define CEC_FILTER_THRESHOLD 0x15
0022
0023 void s5p_cec_set_divider(struct s5p_cec_dev *cec)
0024 {
0025 u32 div_ratio, div_val;
0026 unsigned int reg;
0027
0028 div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
0029
0030 if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) {
0031 dev_err(cec->dev, "failed to read phy control\n");
0032 return;
0033 }
0034
0035 reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
0036
0037 if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
0038 dev_err(cec->dev, "failed to write phy control\n");
0039 return;
0040 }
0041
0042 div_val = CEC_DIV_RATIO * 0.00005 - 1;
0043
0044 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
0045 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
0046 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
0047 writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
0048 }
0049
0050 void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
0051 {
0052 u8 reg;
0053
0054 reg = readb(cec->reg + S5P_CEC_RX_CTRL);
0055 reg |= S5P_CEC_RX_CTRL_ENABLE;
0056 writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
0057 }
0058
0059 void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
0060 {
0061 u8 reg;
0062
0063 reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
0064 reg |= S5P_CEC_IRQ_RX_DONE;
0065 reg |= S5P_CEC_IRQ_RX_ERROR;
0066 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
0067 }
0068
0069 void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
0070 {
0071 u8 reg;
0072
0073 reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
0074 reg &= ~S5P_CEC_IRQ_RX_DONE;
0075 reg &= ~S5P_CEC_IRQ_RX_ERROR;
0076 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
0077 }
0078
0079 void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
0080 {
0081 u8 reg;
0082
0083 reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
0084 reg |= S5P_CEC_IRQ_TX_DONE;
0085 reg |= S5P_CEC_IRQ_TX_ERROR;
0086 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
0087 }
0088
0089 void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
0090 {
0091 u8 reg;
0092
0093 reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
0094 reg &= ~S5P_CEC_IRQ_TX_DONE;
0095 reg &= ~S5P_CEC_IRQ_TX_ERROR;
0096 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
0097 }
0098
0099 void s5p_cec_reset(struct s5p_cec_dev *cec)
0100 {
0101 u8 reg;
0102
0103 writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
0104 writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
0105
0106 reg = readb(cec->reg + 0xc4);
0107 reg &= ~0x1;
0108 writeb(reg, cec->reg + 0xc4);
0109 }
0110
0111 void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
0112 {
0113 writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
0114 }
0115
0116 void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
0117 {
0118 u8 reg;
0119
0120 writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
0121
0122 reg = readb(cec->reg + 0xc4);
0123 reg &= ~0x1;
0124 writeb(reg, cec->reg + 0xc4);
0125 }
0126
0127 void s5p_cec_threshold(struct s5p_cec_dev *cec)
0128 {
0129 writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
0130 writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
0131 }
0132
0133 void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
0134 size_t count, u8 retries)
0135 {
0136 int i = 0;
0137 u8 reg;
0138
0139 while (i < count) {
0140 writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
0141 i++;
0142 }
0143
0144 writeb(count, cec->reg + S5P_CEC_TX_BYTES);
0145 reg = readb(cec->reg + S5P_CEC_TX_CTRL);
0146 reg |= S5P_CEC_TX_CTRL_START;
0147 reg &= ~0x70;
0148 reg |= retries << 4;
0149
0150 if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
0151 dev_dbg(cec->dev, "Broadcast");
0152 reg |= S5P_CEC_TX_CTRL_BCAST;
0153 } else {
0154 dev_dbg(cec->dev, "No Broadcast");
0155 reg &= ~S5P_CEC_TX_CTRL_BCAST;
0156 }
0157
0158 writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
0159 dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
0160 (int)count, data);
0161 }
0162
0163 void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
0164 {
0165 writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
0166 }
0167
0168 u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
0169 {
0170 u32 status = 0;
0171
0172 status = readb(cec->reg + S5P_CEC_STATUS_0) & 0xf;
0173 status |= (readb(cec->reg + S5P_CEC_TX_STAT1) & 0xf) << 4;
0174 status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
0175 status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
0176 status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
0177
0178 dev_dbg(cec->dev, "status = 0x%x!\n", status);
0179
0180 return status;
0181 }
0182
0183 void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
0184 {
0185 writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
0186 cec->reg + S5P_CEC_IRQ_CLEAR);
0187 }
0188
0189 void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
0190 {
0191 writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
0192 cec->reg + S5P_CEC_IRQ_CLEAR);
0193 }
0194
0195 void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
0196 {
0197 u32 i = 0;
0198 char debug[40];
0199
0200 while (i < size) {
0201 buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
0202 sprintf(debug + i * 2, "%02x ", buffer[i]);
0203 i++;
0204 }
0205 dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
0206 }