linux/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c
<<
>>
Prefs
   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
  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, &reg)) {
  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