drm/vc4: Set up the AVI and SPD infoframes.
Fixes a purple bar on the left side of the screen with my Dell 2408WFP. It will also be required for supporting the double-clocked video modes. Signed-off-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
parent
682e62c454
commit
21317b3fba
2 changed files with 136 additions and 5 deletions
|
@ -62,6 +62,8 @@ struct vc4_hdmi {
|
||||||
struct vc4_hdmi_encoder {
|
struct vc4_hdmi_encoder {
|
||||||
struct vc4_encoder base;
|
struct vc4_encoder base;
|
||||||
bool hdmi_monitor;
|
bool hdmi_monitor;
|
||||||
|
bool limited_rgb_range;
|
||||||
|
bool rgb_range_selectable;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct vc4_hdmi_encoder *
|
static inline struct vc4_hdmi_encoder *
|
||||||
|
@ -205,6 +207,12 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
|
vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
|
||||||
|
|
||||||
|
if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
|
||||||
|
vc4_encoder->rgb_range_selectable =
|
||||||
|
drm_rgb_quant_range_selectable(edid);
|
||||||
|
}
|
||||||
|
|
||||||
drm_mode_connector_update_edid_property(connector, edid);
|
drm_mode_connector_update_edid_property(connector, edid);
|
||||||
ret = drm_add_edid_modes(connector, edid);
|
ret = drm_add_edid_modes(connector, edid);
|
||||||
|
|
||||||
|
@ -272,6 +280,117 @@ static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
|
||||||
.destroy = vc4_hdmi_encoder_destroy,
|
.destroy = vc4_hdmi_encoder_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
|
||||||
|
enum hdmi_infoframe_type type)
|
||||||
|
{
|
||||||
|
struct drm_device *dev = encoder->dev;
|
||||||
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||||
|
u32 packet_id = type - 0x80;
|
||||||
|
|
||||||
|
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
|
||||||
|
HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
|
||||||
|
|
||||||
|
return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
|
||||||
|
BIT(packet_id)), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
|
||||||
|
union hdmi_infoframe *frame)
|
||||||
|
{
|
||||||
|
struct drm_device *dev = encoder->dev;
|
||||||
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||||
|
u32 packet_id = frame->any.type - 0x80;
|
||||||
|
u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id;
|
||||||
|
uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
|
||||||
|
ssize_t len, i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
|
||||||
|
VC4_HDMI_RAM_PACKET_ENABLE),
|
||||||
|
"Packet RAM has to be on to store the packet.");
|
||||||
|
|
||||||
|
len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
|
||||||
|
if (len < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = vc4_hdmi_stop_packet(encoder, frame->any.type);
|
||||||
|
if (ret) {
|
||||||
|
DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i += 7) {
|
||||||
|
HDMI_WRITE(packet_reg,
|
||||||
|
buffer[i + 0] << 0 |
|
||||||
|
buffer[i + 1] << 8 |
|
||||||
|
buffer[i + 2] << 16);
|
||||||
|
packet_reg += 4;
|
||||||
|
|
||||||
|
HDMI_WRITE(packet_reg,
|
||||||
|
buffer[i + 3] << 0 |
|
||||||
|
buffer[i + 4] << 8 |
|
||||||
|
buffer[i + 5] << 16 |
|
||||||
|
buffer[i + 6] << 24);
|
||||||
|
packet_reg += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
|
||||||
|
HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
|
||||||
|
ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
|
||||||
|
BIT(packet_id)), 100);
|
||||||
|
if (ret)
|
||||||
|
DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
|
||||||
|
{
|
||||||
|
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
|
||||||
|
struct drm_crtc *crtc = encoder->crtc;
|
||||||
|
const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||||
|
union hdmi_infoframe frame;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
|
||||||
|
if (ret < 0) {
|
||||||
|
DRM_ERROR("couldn't fill AVI infoframe\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vc4_encoder->rgb_range_selectable) {
|
||||||
|
if (vc4_encoder->limited_rgb_range) {
|
||||||
|
frame.avi.quantization_range =
|
||||||
|
HDMI_QUANTIZATION_RANGE_LIMITED;
|
||||||
|
} else {
|
||||||
|
frame.avi.quantization_range =
|
||||||
|
HDMI_QUANTIZATION_RANGE_FULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vc4_hdmi_write_infoframe(encoder, &frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
|
||||||
|
{
|
||||||
|
union hdmi_infoframe frame;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore");
|
||||||
|
if (ret < 0) {
|
||||||
|
DRM_ERROR("couldn't fill SPD infoframe\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.spd.sdi = HDMI_SPD_SDI_PC;
|
||||||
|
|
||||||
|
vc4_hdmi_write_infoframe(encoder, &frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
|
||||||
|
{
|
||||||
|
vc4_hdmi_set_avi_infoframe(encoder);
|
||||||
|
vc4_hdmi_set_spd_infoframe(encoder);
|
||||||
|
}
|
||||||
|
|
||||||
static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||||
struct drm_display_mode *unadjusted_mode,
|
struct drm_display_mode *unadjusted_mode,
|
||||||
struct drm_display_mode *mode)
|
struct drm_display_mode *mode)
|
||||||
|
@ -340,8 +459,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||||
|
|
||||||
if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) {
|
if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) {
|
||||||
/* CEA VICs other than #1 requre limited range RGB
|
/* CEA VICs other than #1 requre limited range RGB
|
||||||
* output. Apply a colorspace conversion to squash
|
* output unless overridden by an AVI infoframe.
|
||||||
* 0-255 down to 16-235. The matrix here is:
|
* Apply a colorspace conversion to squash 0-255 down
|
||||||
|
* to 16-235. The matrix here is:
|
||||||
*
|
*
|
||||||
* [ 0 0 0.8594 16]
|
* [ 0 0 0.8594 16]
|
||||||
* [ 0 0.8594 0 16]
|
* [ 0 0.8594 0 16]
|
||||||
|
@ -359,6 +479,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||||
HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000);
|
HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000);
|
||||||
HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0);
|
HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0);
|
||||||
HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000);
|
HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000);
|
||||||
|
vc4_encoder->limited_rgb_range = true;
|
||||||
|
} else {
|
||||||
|
vc4_encoder->limited_rgb_range = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The RGB order applies even when CSC is disabled. */
|
/* The RGB order applies even when CSC is disabled. */
|
||||||
|
@ -377,6 +500,8 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||||
struct drm_device *dev = encoder->dev;
|
struct drm_device *dev = encoder->dev;
|
||||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||||
|
|
||||||
|
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
|
||||||
|
|
||||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
||||||
HD_WRITE(VC4_HD_VID_CTL,
|
HD_WRITE(VC4_HD_VID_CTL,
|
||||||
HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
|
HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
|
||||||
|
@ -429,9 +554,10 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||||
HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
|
HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
|
||||||
VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
|
VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
|
||||||
|
|
||||||
/* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
|
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
|
||||||
* up the infoframe.
|
VC4_HDMI_RAM_PACKET_ENABLE);
|
||||||
*/
|
|
||||||
|
vc4_hdmi_set_infoframes(encoder);
|
||||||
|
|
||||||
drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
|
drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
|
||||||
drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
|
drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
|
||||||
|
|
|
@ -441,6 +441,8 @@
|
||||||
#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0
|
#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0
|
||||||
# define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
|
# define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
|
||||||
|
|
||||||
|
#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4
|
||||||
|
|
||||||
#define VC4_HDMI_HORZA 0x0c4
|
#define VC4_HDMI_HORZA 0x0c4
|
||||||
# define VC4_HDMI_HORZA_VPOS BIT(14)
|
# define VC4_HDMI_HORZA_VPOS BIT(14)
|
||||||
# define VC4_HDMI_HORZA_HPOS BIT(13)
|
# define VC4_HDMI_HORZA_HPOS BIT(13)
|
||||||
|
@ -502,6 +504,9 @@
|
||||||
|
|
||||||
#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0
|
#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0
|
||||||
|
|
||||||
|
#define VC4_HDMI_GCP_0 0x400
|
||||||
|
#define VC4_HDMI_PACKET_STRIDE 0x24
|
||||||
|
|
||||||
#define VC4_HD_M_CTL 0x00c
|
#define VC4_HD_M_CTL 0x00c
|
||||||
# define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6)
|
# define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6)
|
||||||
# define VC4_HD_M_RAM_STANDBY (3 << 4)
|
# define VC4_HD_M_RAM_STANDBY (3 << 4)
|
||||||
|
|
Loading…
Add table
Reference in a new issue