1 // SPDX-License-Identifier: GPL-2.0-only 2 /* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c 3 * 4 * Copyright (c) 2009, 2014 Samsung Electronics 5 * http://www.samsung.com/ 6 * 7 * cec ftn file for Samsung TVOUT driver 8 */ 9 10 #include <linux/io.h> 11 #include <linux/device.h> 12 13 #include "exynos_hdmi_cec.h" 14 #include "regs-cec.h" 15 16 #define S5P_HDMI_FIN 24000000 17 #define CEC_DIV_RATIO 320000 18 19 #define CEC_MESSAGE_BROADCAST_MASK 0x0F 20 #define CEC_MESSAGE_BROADCAST 0x0F 21 #define CEC_FILTER_THRESHOLD 0x15 22 23 void s5p_cec_set_divider(struct s5p_cec_dev *cec) 24 { 25 u32 div_ratio, div_val; 26 unsigned int reg; 27 28 div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1; 29 30 if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) { 31 dev_err(cec->dev, "failed to read phy control\n"); 32 return; 33 } 34 35 reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16); 36 37 if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) { 38 dev_err(cec->dev, "failed to write phy control\n"); 39 return; 40 } 41 42 div_val = CEC_DIV_RATIO * 0.00005 - 1; 43 44 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3); 45 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2); 46 writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1); 47 writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0); 48 } 49 50 void s5p_cec_enable_rx(struct s5p_cec_dev *cec) 51 { 52 u8 reg; 53 54 reg = readb(cec->reg + S5P_CEC_RX_CTRL); 55 reg |= S5P_CEC_RX_CTRL_ENABLE; 56 writeb(reg, cec->reg + S5P_CEC_RX_CTRL); 57 } 58 59 void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec) 60 { 61 u8 reg; 62 63 reg = readb(cec->reg + S5P_CEC_IRQ_MASK); 64 reg |= S5P_CEC_IRQ_RX_DONE; 65 reg |= S5P_CEC_IRQ_RX_ERROR; 66 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); 67 } 68 69 void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec) 70 { 71 u8 reg; 72 73 reg = readb(cec->reg + S5P_CEC_IRQ_MASK); 74 reg &= ~S5P_CEC_IRQ_RX_DONE; 75 reg &= ~S5P_CEC_IRQ_RX_ERROR; 76 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); 77 } 78 79 void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec) 80 { 81 u8 reg; 82 83 reg = readb(cec->reg + S5P_CEC_IRQ_MASK); 84 reg |= S5P_CEC_IRQ_TX_DONE; 85 reg |= S5P_CEC_IRQ_TX_ERROR; 86 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); 87 } 88 89 void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec) 90 { 91 u8 reg; 92 93 reg = readb(cec->reg + S5P_CEC_IRQ_MASK); 94 reg &= ~S5P_CEC_IRQ_TX_DONE; 95 reg &= ~S5P_CEC_IRQ_TX_ERROR; 96 writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); 97 } 98 99 void s5p_cec_reset(struct s5p_cec_dev *cec) 100 { 101 u8 reg; 102 103 writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); 104 writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); 105 106 reg = readb(cec->reg + 0xc4); 107 reg &= ~0x1; 108 writeb(reg, cec->reg + 0xc4); 109 } 110 111 void s5p_cec_tx_reset(struct s5p_cec_dev *cec) 112 { 113 writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); 114 } 115 116 void s5p_cec_rx_reset(struct s5p_cec_dev *cec) 117 { 118 u8 reg; 119 120 writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); 121 122 reg = readb(cec->reg + 0xc4); 123 reg &= ~0x1; 124 writeb(reg, cec->reg + 0xc4); 125 } 126 127 void s5p_cec_threshold(struct s5p_cec_dev *cec) 128 { 129 writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH); 130 writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL); 131 } 132 133 void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, 134 size_t count, u8 retries) 135 { 136 int i = 0; 137 u8 reg; 138 139 while (i < count) { 140 writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4))); 141 i++; 142 } 143 144 writeb(count, cec->reg + S5P_CEC_TX_BYTES); 145 reg = readb(cec->reg + S5P_CEC_TX_CTRL); 146 reg |= S5P_CEC_TX_CTRL_START; 147 reg &= ~0x70; 148 reg |= retries << 4; 149 150 if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) { 151 dev_dbg(cec->dev, "Broadcast"); 152 reg |= S5P_CEC_TX_CTRL_BCAST; 153 } else { 154 dev_dbg(cec->dev, "No Broadcast"); 155 reg &= ~S5P_CEC_TX_CTRL_BCAST; 156 } 157 158 writeb(reg, cec->reg + S5P_CEC_TX_CTRL); 159 dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count, 160 (int)count, data); 161 } 162 163 void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr) 164 { 165 writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR); 166 } 167 168 u32 s5p_cec_get_status(struct s5p_cec_dev *cec) 169 { 170 u32 status = 0; 171 172 status = readb(cec->reg + S5P_CEC_STATUS_0) & 0xf; 173 status |= (readb(cec->reg + S5P_CEC_TX_STAT1) & 0xf) << 4; 174 status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8; 175 status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16; 176 status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24; 177 178 dev_dbg(cec->dev, "status = 0x%x!\n", status); 179 180 return status; 181 } 182 183 void s5p_clr_pending_tx(struct s5p_cec_dev *cec) 184 { 185 writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR, 186 cec->reg + S5P_CEC_IRQ_CLEAR); 187 } 188 189 void s5p_clr_pending_rx(struct s5p_cec_dev *cec) 190 { 191 writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR, 192 cec->reg + S5P_CEC_IRQ_CLEAR); 193 } 194 195 void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer) 196 { 197 u32 i = 0; 198 char debug[40]; 199 200 while (i < size) { 201 buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4)); 202 sprintf(debug + i * 2, "%02x ", buffer[i]); 203 i++; 204 } 205 dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug); 206 } 207