Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
0003  *
0004  * Copyright (c) 2009, 2014 Samsung Electronics
0005  *      http://www.samsung.com/
0006  *
0007  * cec ftn file for Samsung TVOUT driver
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, &reg)) {
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 }