207 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c3 *4 * Copyright (c) 2009, 2014 Samsung Electronics5 * http://www.samsung.com/6 *7 * cec ftn file for Samsung TVOUT driver8 */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 2400000017#define CEC_DIV_RATIO 32000018 19#define CEC_MESSAGE_BROADCAST_MASK 0x0F20#define CEC_MESSAGE_BROADCAST 0x0F21#define CEC_FILTER_THRESHOLD 0x1522 23void 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 50void 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 59void 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 69void 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 79void 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 89void 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 99void 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 111void 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 116void 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 127void 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 133void 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 163void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)164{165 writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);166}167 168u32 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 183void 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 189void 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 195void 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