1
0
Fork 0
alistair23-linux/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c

332 lines
7.9 KiB
C

/*
* Copyright (C) 2019 NXP Semiconductor, Inc.
*
* 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.
*/
#include <drm/drmP.h>
#include <linux/io.h>
#include <drm/bridge/cdns-mhdp-common.h>
#include <linux/regmap.h>
void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp,
u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type)
{
u32 *packet32, len32;
u32 val, i;
/* invalidate entry */
val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id);
cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
/* flush fifo 1 */
cdns_mhdp_bus_write(F_FIFO1_FLUSH(1), mhdp, SOURCE_PIF_FIFO1_FLUSH);
/* write packet into memory */
packet32 = (u32 *)packet;
len32 = packet_len / 4;
for (i = 0; i < len32; i++)
cdns_mhdp_bus_write(F_DATA_WR(packet32[i]), mhdp, SOURCE_PIF_DATA_WR);
/* write entry id */
cdns_mhdp_bus_write(F_WR_ADDR(entry_id), mhdp, SOURCE_PIF_WR_ADDR);
/* write request */
cdns_mhdp_bus_write(F_HOST_WR(1), mhdp, SOURCE_PIF_WR_REQ);
/* update entry */
val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) |
F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id);
cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
}
int cdns_hdmi_get_edid_block(void *data, u8 *edid,
u32 block, size_t length)
{
struct cdns_mhdp_device *mhdp = data;
u8 msg[2], reg[5], i;
int ret;
for (i = 0; i < 4; i++) {
msg[0] = block / 2;
msg[1] = block % 2;
ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID,
sizeof(msg), msg);
if (ret)
continue;
ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
HDMI_TX_EDID, sizeof(reg) + length);
if (ret)
continue;
ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
continue;
ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length);
if (ret)
continue;
if ((reg[3] << 8 | reg[4]) == length)
break;
}
if (ret)
DRM_ERROR("get block[%d] edid failed: %d\n", block, ret);
return ret;
}
int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data)
{
u8 msg[4], reg[6];
int ret;
msg[0] = 0x54;
msg[1] = addr;
msg[2] = 0;
msg[3] = 1;
ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ,
sizeof(msg), msg);
if (ret)
goto err_scdc_read;
ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
HDMI_TX_READ, sizeof(reg));
if (ret)
goto err_scdc_read;
ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_scdc_read;
*data = reg[5];
err_scdc_read:
if (ret)
DRM_ERROR("scdc read failed: %d\n", ret);
return ret;
}
int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value)
{
u8 msg[5], reg[5];
int ret;
msg[0] = 0x54;
msg[1] = addr;
msg[2] = 0;
msg[3] = 1;
msg[4] = value;
ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE,
sizeof(msg), msg);
if (ret)
goto err_scdc_write;
ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
HDMI_TX_WRITE, sizeof(reg));
if (ret)
goto err_scdc_write;
ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_scdc_write;
if (reg[0] != 0)
ret = -EINVAL;
err_scdc_write:
if (ret)
DRM_ERROR("scdc write failed: %d\n", ret);
return ret;
}
int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp,
int protocol,
u32 char_rate)
{
u32 reg0;
u32 reg1;
u32 val;
int ret;
/* Set PHY to HDMI data */
ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1));
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD,
F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0));
if (ret < 0)
return ret;
/* open CARS */
ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3);
if (ret < 0)
return ret;
reg0 = reg1 = 0x7c1f;
if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) {
reg0 = 0;
reg1 = 0xFFFFF;
}
ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1);
if (ret < 0)
return ret;
/* set hdmi mode and preemble mode data enable */
val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) |
F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF);
ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
return ret;
}
int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp,
struct drm_display_mode *mode,
struct video_info *video_info)
{
int ret;
u32 val;
u32 vsync_lines = mode->vsync_end - mode->vsync_start;
u32 eof_lines = mode->vsync_start - mode->vdisplay;
u32 sof_lines = mode->vtotal - mode->vsync_end;
u32 hblank = mode->htotal - mode->hdisplay;
u32 hactive = mode->hdisplay;
u32 vblank = mode->vtotal - mode->vdisplay;
u32 vactive = mode->vdisplay;
u32 hfront = mode->hsync_start - mode->hdisplay;
u32 hback = mode->htotal - mode->hsync_end;
u32 vfront = eof_lines;
u32 hsync = hblank - hfront - hback;
u32 vsync = vsync_lines;
u32 vback = sof_lines;
u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) +
((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2);
ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + hblank);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + vblank);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + hfront);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + hsync);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + hback);
if (ret < 0)
return ret;
ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity);
if (ret < 0)
return ret;
/* Reset Data Enable */
val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
val &= ~F_DATA_EN(1);
ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
if (ret < 0)
return ret;
/* Set bpc */
val &= ~F_VIF_DATA_WIDTH(3);
switch (video_info->color_depth) {
case 10:
val |= F_VIF_DATA_WIDTH(1);
break;
case 12:
val |= F_VIF_DATA_WIDTH(2);
break;
case 16:
val |= F_VIF_DATA_WIDTH(3);
break;
case 8:
default:
val |= F_VIF_DATA_WIDTH(0);
break;
}
/* select color encoding */
val &= ~F_HDMI_ENCODING(3);
switch (video_info->color_fmt) {
case YCBCR_4_4_4:
val |= F_HDMI_ENCODING(2);
break;
case YCBCR_4_2_2:
val |= F_HDMI_ENCODING(1);
break;
case YCBCR_4_2_0:
val |= F_HDMI_ENCODING(3);
break;
case PXL_RGB:
default:
val |= F_HDMI_ENCODING(0);
break;
}
ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
if (ret < 0)
return ret;
/* set data enable */
val |= F_DATA_EN(1);
ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
return ret;
}
int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp)
{
u32 val;
val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
val &= ~F_GCP_EN(1);
return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
}
int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp)
{
u32 val;
val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
val |= F_GCP_EN(1);
return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
}