/*
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
* Author: Chris Zhong <zyw@rock-chips.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
#define CDN_DP_SPDIF_CLK 200000000
#define FW_ALIVE_TIMEOUT_US 1000000
#define MAILBOX_RETRY_US 1000
#define MAILBOX_TIMEOUT_US 5000000
#define LINK_TRAINING_RETRY_MS 20
#define LINK_TRAINING_TIMEOUT_MS 500
void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk)
{
writel(clk / 1000000, dp->regs + SW_CLK_H);
}
void cdn_dp_clock_reset(struct cdn_dp_device *dp)
{
u32 val;
val = DPTX_FRMR_DATA_CLK_RSTN_EN |
DPTX_FRMR_DATA_CLK_EN |
DPTX_PHY_DATA_RSTN_EN |
DPTX_PHY_DATA_CLK_EN |
DPTX_PHY_CHAR_RSTN_EN |
DPTX_PHY_CHAR_CLK_EN |
SOURCE_AUX_SYS_CLK_RSTN_EN |
SOURCE_AUX_SYS_CLK_EN |
DPTX_SYS_CLK_RSTN_EN |
DPTX_SYS_CLK_EN |
CFG_DPTX_VIF_CLK_RSTN_EN |
CFG_DPTX_VIF_CLK_EN;
writel(val, dp->regs + SOURCE_DPTX_CAR);
val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
writel(val, dp->regs + SOURCE_PHY_CAR);
val = SOURCE_PKT_SYS_RSTN_EN |
SOURCE_PKT_SYS_CLK_EN |
SOURCE_PKT_DATA_RSTN_EN |
SOURCE_PKT_DATA_CLK_EN;
writel(val, dp->regs + SOURCE_PKT_CAR);
val = SPDIF_CDR_CLK_RSTN_EN |
SPDIF_CDR_CLK_EN |
SOURCE_AIF_SYS_RSTN_EN |
SOURCE_AIF_SYS_CLK_EN |
SOURCE_AIF_CLK_RSTN_EN |
SOURCE_AIF_CLK_EN;
writel(val, dp->regs + SOURCE_AIF_CAR);
val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
SOURCE_CIPHER_SYS_CLK_EN |
SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
SOURCE_CIPHER_CHAR_CLK_EN;
writel(val, dp->regs + SOURCE_CIPHER_CAR);
val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
SOURCE_CRYPTO_SYS_CLK_EN;
writel(val, dp->regs + SOURCE_CRYPTO_CAR);
/* enable Mailbox and PIF interrupt */
writel(0, dp->regs + APB_INT_MASK);
}
static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
{
int val, ret;
ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
val, !val, MAILBOX_RETRY_US,
MAILBOX_TIMEOUT_US);
if (ret < 0)
return ret;
return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
}
static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
{
int ret, full;
ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
full, !full, MAILBOX_RETRY_US,
MAILBOX_TIMEOUT_US);
if (ret < 0)
return ret;
writel(val, dp->regs + MAILBOX0_WR_DATA);
return 0;
}
static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
u8 module_id, u8 opcode,
u8 req_size)
{
u32 mbox_size, i;
u8 header[4];
int ret;
/* read the header of the message */
for (i = 0; i < 4; i++) {
ret = cdn_dp_mailbox_read(dp);
if (ret < 0)
return ret;
header[i] = ret;
}
mbox_size = (header[2] << 8) | header[3];
if (opcode != header[0] || module_id != header[1] ||
req_size != mbox_size) {
/*
* If the message in mailbox is not what we want, we need to
* clear the mailbox by reading its contents.
*/
for (i = 0; i < mbox_size; i++)
if (cdn_dp_mailbox_read(dp) < 0)
break;
return -EINVAL;
}
return 0;
}
static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
u8 *buff, u8 buff_size)
{
u32 i;
int ret;
for (i = 0; i < buff_size; i++) {
ret = cdn_dp_mailbox_read(dp);
if (ret < 0)
return ret;
buff[i] = ret;
}
return 0;
}
static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
u8 opcode, u16 size, u8 *message)
{
u8 header[4];
int ret, i;
header[0] = opcode;
header[1] = module_id;
header[2] = (size >> 8) & 0xff;
header[3] = size & 0xff;
for (i = 0; i < 4; i++) {
ret = cdp_dp_mailbox_write(dp, header[i]);
if (ret)
return ret;
}
for (i = 0; i < size; i++) {
ret = cdp_dp_mailbox_write(dp, message[i]);
if (ret)
return ret;
}
return 0;
}
static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
{
u8 msg[6];
msg[0] = (addr >> 8) & 0xff;
msg[1] = addr & 0xff;
msg[2] = (val >> 24) & 0xff;
msg[3] = (val >> 16) & 0xff;
msg[4] = (val >> 8) & 0xff;
msg[5] = val & 0xff;
return