blob: 5efae6ad93c866c4e3f9d528a6dfa455250fd9cb [file] [log] [blame]
/*
* drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hw/hdmi_tx_ddc.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_ddc.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_info_global.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h>
#include "common.h"
#include "hdmi_tx_reg.h"
static DEFINE_MUTEX(ddc_mutex);
static uint32_t ddc_write_1byte(uint8_t slave, uint8_t offset_addr,
uint8_t data)
{
uint32_t st = 0;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, slave);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, offset_addr);
hdmitx_wr_reg(HDMITX_DWC_I2CM_DATAO, data);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 4);
mdelay(2);
if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) {
st = 0;
pr_info("E: ddc w1b 0x%02x 0x%02x 0x%02x\n",
slave, offset_addr, data);
} else
st = 1;
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7);
mutex_unlock(&ddc_mutex);
return st;
}
#if 0
static uint32_t ddc_readext_8byte(uint8_t slave, uint8_t offset_addr,
uint8_t *data)
{
uint32_t st = 0;
int32_t i;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, slave);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, offset_addr);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGADDR, EDIDSEG_ADR);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGPTR, 0x00);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 3);
mdelay(2);
if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) {
st = 0;
pr_info("E: ddc rdext8b 0x%02x 0x%02x\n",
slave, offset_addr);
} else
st = 1;
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7);
for (i = 0; i < 8; i++)
data[i] = hdmitx_rd_reg(HDMITX_DWC_I2CM_READ_BUFF0 + i);
mutex_unlock(&ddc_mutex);
return st;
}
#endif
static uint32_t ddc_read_8byte(uint8_t slave, uint8_t offset_addr,
uint8_t *data)
{
uint32_t st = 0;
int32_t i;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, slave);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, offset_addr);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 2);
mdelay(2);
if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) {
st = 0;
pr_info("E: ddc rd8b 0x%02x 0x%02x 0x%02x\n",
slave, offset_addr, *data);
} else
st = 1;
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7);
for (i = 0; i < 8; i++)
data[i] = hdmitx_rd_reg(HDMITX_DWC_I2CM_READ_BUFF0 + i);
mutex_unlock(&ddc_mutex);
return st;
}
#if 0
static uint32_t ddc_readext_1byte(uint8_t slave, uint8_t address, uint8_t *data)
{
uint32_t st = 0;
int32_t i;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, slave);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, offset_addr);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGADDR, EDIDSEG_ADR);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGPTR, 0x00);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 1);
mdelay(2);
if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) {
st = 0;
pr_info("E: hdmitx: ddc rd8b 0x%02x 0x%02x\n",
slave, offset_addr);
} else
st = 1;
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7);
*data = hdmitx_rd_reg(HDMITX_DWC_I2CM_DATAI);
mutex_unlock(&ddc_mutex);
return st;
}
#endif
static uint32_t ddc_read_1byte(uint8_t slave, uint8_t offset_addr,
uint8_t *data)
{
uint32_t st = 0;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, slave);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, offset_addr);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 0);
mdelay(2);
if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) {
st = 0;
pr_info("E: ddc rd8b 0x%02x 0x%02x\n",
slave, offset_addr);
} else
st = 1;
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7);
*data = hdmitx_rd_reg(HDMITX_DWC_I2CM_DATAI);
mutex_unlock(&ddc_mutex);
return st;
}
static uint32_t hdcp_rd_bksv(uint8_t *data)
{
return ddc_read_8byte(HDCP_SLAVE, HDCP14_BKSV, data);
}
void scdc_rd_sink(uint8_t adr, uint8_t *val)
{
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ddc_read_1byte(SCDC_SLAVE, adr, val);
}
void scdc_wr_sink(uint8_t adr, uint8_t val)
{
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ddc_write_1byte(SCDC_SLAVE, adr, val);
}
uint32_t hdcp_rd_hdcp14_ver(void)
{
int ret = 0;
uint8_t bksv[8] = {0};
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ret = hdcp_rd_bksv(&bksv[0]);
if (ret)
return 1;
ret = hdcp_rd_bksv(&bksv[0]);
if (ret)
return 1;
return 0;
}
uint32_t hdcp_rd_hdcp22_ver(void)
{
uint32_t ret;
uint8_t ver;
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ret = ddc_read_1byte(HDCP_SLAVE, HDCP2_VERSION, &ver);
if (ret)
return ver == 0x04;
ret = ddc_read_1byte(HDCP_SLAVE, HDCP2_VERSION, &ver);
if (ret)
return ver == 0x04;
return 0;
}
/* only for I2C reactive using */
void edid_read_head_8bytes(void)
{
uint8_t head[8] = {0};
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ddc_read_8byte(EDID_SLAVE, 0x00, head);
}
#define EDID_WAIT_TIMEOUT 10
void hdmitx_read_edid(unsigned char *rx_edid)
{
unsigned int timeout = 0;
unsigned int i;
unsigned int byte_num = 0;
unsigned char blk_no = 1;
if (!rx_edid)
return;
mutex_lock(&ddc_mutex);
/* Program SLAVE/ADDR */
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, 0x50);
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 1 << 1);
/* Read complete EDID data sequentially */
while (byte_num < 128 * blk_no) {
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, byte_num & 0xff);
if ((byte_num >= 256) && (byte_num < 512) && (blk_no > 2)) {
/* Program SEGMENT/SEGPTR */
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGADDR, 0x30);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGPTR, 0x1);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 3);
} else {
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 2);
}
/* Wait until I2C done */
timeout = 0;
while (!(hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 1)) &&
(timeout < EDID_WAIT_TIMEOUT)) {
usleep_range(2000, 2010);
timeout++;
}
if (timeout == EDID_WAIT_TIMEOUT)
pr_info(HW "ddc timeout\n");
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 1 << 1);
/* Read back 8 bytes */
for (i = 0; i < 8; i++) {
rx_edid[byte_num] =
hdmitx_rd_reg(HDMITX_DWC_I2CM_READ_BUFF0 + i);
if (byte_num == 126) {
blk_no = rx_edid[126] + 1;
if (blk_no > 4) {
pr_info(HW "edid extension block number:");
pr_info(HW " %d, reset to MAX 3\n",
blk_no - 1);
blk_no = 4; /* Max extended block */
}
}
byte_num++;
}
}
/* Because DRM will use segment registers,
* so clear the registers to default
*/
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGADDR, 0x0);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SEGPTR, 0x0);
mutex_unlock(&ddc_mutex);
}