blob: e277c5c54c4c8b97ec7751e08f31ecd4077ee8b2 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#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(u8 slave, u8 offset_addr, u8 data)
{
u32 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("hdmitx: 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;
}
static uint32_t ddc_read_8byte(u8 slave, u8 offset_addr, u8 *data)
{
u32 st = 0;
s32 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("hdmitx: 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;
}
static uint32_t ddc_read_1byte(u8 slave, u8 offset_addr, u8 *data)
{
u32 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("hdmitx: 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(u8 *data)
{
return ddc_read_8byte(HDCP_SLAVE, HDCP14_BKSV, data);
}
void scdc_rd_sink(u8 adr, u8 *val)
{
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ddc_read_1byte(SCDC_SLAVE, adr, val);
}
void scdc_wr_sink(u8 adr, u8 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;
u8 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)
{
u32 ret;
u8 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)
{
u8 head[8] = {0};
hdmitx_ddc_hw_op(DDC_MUX_DDC);
ddc_read_8byte(EDID_SLAVE, 0x00, head);
}
#define EDID_WAIT_TIMEOUT 10
static void _i2c_soft_reset(void)
{
unsigned int timeout = 0;
mutex_lock(&ddc_mutex);
hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, 0x00);
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 1 << 1);
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, 0x06);
hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 4);
/* Wait until I2C done */
timeout = 0;
while (timeout < EDID_WAIT_TIMEOUT &&
!(hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 1))) {
mdelay(2);
timeout++;
}
if (timeout == EDID_WAIT_TIMEOUT)
pr_info("%s timeout\n", __func__);
mutex_unlock(&ddc_mutex);
}
/*
* Note: read 8 Bytes of EDID data every time
*/
static int hdmitx_read_edid_blk(unsigned char *buf, int blk_no)
{
unsigned int timeout = 0;
unsigned int i;
unsigned int byte_num = 0;
struct hdmitx_dev *hdev = get_hdmitx_device();
unsigned char *i2c_err = &hdev->rxcap.i2c_err;
int count_at_start_of_edid_read;
if (!buf)
return 0;
mutex_lock(&ddc_mutex);
count_at_start_of_edid_read = atomic_read(&hdev->hpd_count_for_edid);
/* 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) && (count_at_start_of_edid_read ==
atomic_read(&hdev->hpd_count_for_edid))) {
if (!atomic_read(&hdev->hpd_state_for_edid)) {
pr_err("hdmitx: hpd fall in edid reading\n");
mutex_unlock(&ddc_mutex);
return 0;
}
hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS,
(byte_num + 128 * blk_no) & 0xff);
if (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 (timeout < EDID_WAIT_TIMEOUT &&
!(hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 1))) {
mdelay(2);
timeout++;
}
if (timeout == EDID_WAIT_TIMEOUT) {
pr_info(HW "ddc timeout\n");
*i2c_err = 1;
hdmitx_current_status(HDMITX_EDID_I2C_ERROR);
mutex_unlock(&ddc_mutex);
return 0;
} else {
*i2c_err = 0;
}
hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 1 << 1);
/* Read back 8 bytes */
for (i = 0; i < 8; i++) {
buf[byte_num] =
hdmitx_rd_reg(HDMITX_DWC_I2CM_READ_BUFF0 + i);
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);
return 1;
} /* hdmitx_read_ext_block */
static void _perform_i2c_reset(void)
{
mdelay(10);
_i2c_soft_reset();
mdelay(90);
}
#define EDID_READ_MAX_TIMES 10
void hdmitx_read_edid(unsigned char *rx_edid)
{
int i;
int read_times = 0;
int blk_no;
int ret;
if (!rx_edid)
return;
for (read_times = 0; read_times < EDID_READ_MAX_TIMES; read_times++) {
/* clear rx_edid firstly */
memset(rx_edid, 0, sizeof(128 * EDID_MAX_BLOCK));
/* read the block0 */
ret = hdmitx_read_edid_blk(&rx_edid[0], 0);
if (!ret || !check_hdmi_edid_sub_block_valid(&rx_edid[0], 0)) {
_perform_i2c_reset();
continue;
}
/* read the blockn */
blk_no = rx_edid[0x7e] + 1;
if (blk_no > EDID_MAX_BLOCK)
blk_no = EDID_MAX_BLOCK;
for (i = 1; i < blk_no; i++) {
ret = hdmitx_read_edid_blk(&rx_edid[i * 128], i);
if (!ret || !check_hdmi_edid_sub_block_valid(&rx_edid[i * 128], i)) {
_perform_i2c_reset();
break;
}
}
if (i == blk_no) /* read the last block successfully */
break;
}
} /* hdmitx_read_edid */