Commit b47a72f3 authored by Maxime Ripard's avatar Maxime Ripard

drm/vc4: hdmi: Switch to HDMI connector

The new HDMI connector infrastructure allows us to remove a lot of
boilerplate, so let's switch to it.
Acked-by: default avatarSui Jingfeng <sui.jingfeng@linux.dev>
Link: https://patchwork.freedesktop.org/patch/msgid/20240527-kms-hdmi-connector-state-v15-25-c5af16c3aae2@kernel.orgSigned-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent c602e495
...@@ -10,6 +10,7 @@ config DRM_VC4 ...@@ -10,6 +10,7 @@ config DRM_VC4
depends on COMMON_CLK depends on COMMON_CLK
depends on PM depends on PM
select DRM_DISPLAY_HDMI_HELPER select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HDMI_STATE_HELPER
select DRM_DISPLAY_HELPER select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER select DRM_GEM_DMA_HELPER
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
*/ */
#include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/display/drm_scdc_helper.h> #include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
...@@ -110,25 +111,6 @@ ...@@ -110,25 +111,6 @@
#define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
static const char * const output_format_str[] = {
[VC4_HDMI_OUTPUT_RGB] = "RGB",
[VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0",
[VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2",
[VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4",
};
static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt)
{
if (fmt >= ARRAY_SIZE(output_format_str))
return "invalid";
return output_format_str[fmt];
}
static unsigned long long
vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
unsigned int bpc, enum vc4_hdmi_output_format fmt);
static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi) static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
{ {
struct drm_display_info *display = &vc4_hdmi->connector.display_info; struct drm_display_info *display = &vc4_hdmi->connector.display_info;
...@@ -147,28 +129,13 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi) ...@@ -147,28 +129,13 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
unsigned int bpc, unsigned int bpc,
enum vc4_hdmi_output_format fmt) enum hdmi_colorspace fmt)
{ {
unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); unsigned long long clock = drm_hdmi_compute_mode_clock(mode, bpc, fmt);
return clock > HDMI_14_MAX_TMDS_CLK; return clock > HDMI_14_MAX_TMDS_CLK;
} }
static bool vc4_hdmi_is_full_range(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_state)
{
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_LIMITED)
return false;
else if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_FULL)
return true;
return !display->is_hdmi ||
drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL;
}
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
{ {
struct drm_debugfs_entry *entry = m->private; struct drm_debugfs_entry *entry = m->private;
...@@ -524,7 +491,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) ...@@ -524,7 +491,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
const struct drm_display_mode *mode; const struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) { list_for_each_entry(mode, &connector->probed_modes, head) {
if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) { if (vc4_hdmi_mode_needs_scrambling(mode, 8, HDMI_COLORSPACE_RGB)) {
drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz."); drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60."); drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
} }
...@@ -539,12 +506,8 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, ...@@ -539,12 +506,8 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
{ {
struct drm_connector_state *old_state = struct drm_connector_state *old_state =
drm_atomic_get_old_connector_state(state, connector); drm_atomic_get_old_connector_state(state, connector);
struct vc4_hdmi_connector_state *old_vc4_state =
conn_state_to_vc4_hdmi_conn_state(old_state);
struct drm_connector_state *new_state = struct drm_connector_state *new_state =
drm_atomic_get_new_connector_state(state, connector); drm_atomic_get_new_connector_state(state, connector);
struct vc4_hdmi_connector_state *new_vc4_state =
conn_state_to_vc4_hdmi_conn_state(new_state);
struct drm_crtc *crtc = new_state->crtc; struct drm_crtc *crtc = new_state->crtc;
if (!crtc) if (!crtc)
...@@ -576,9 +539,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, ...@@ -576,9 +539,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
return ret; return ret;
} }
if (old_state->colorspace != new_state->colorspace || if (old_state->colorspace != new_state->colorspace) {
old_vc4_state->broadcast_rgb != new_vc4_state->broadcast_rgb ||
!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
struct drm_crtc_state *crtc_state; struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc); crtc_state = drm_atomic_get_crtc_state(state, crtc);
...@@ -588,112 +549,21 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, ...@@ -588,112 +549,21 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
crtc_state->mode_changed = true; crtc_state->mode_changed = true;
} }
return 0; return drm_atomic_helper_connector_hdmi_check(connector, state);
}
static int vc4_hdmi_connector_get_property(struct drm_connector *connector,
const struct drm_connector_state *state,
struct drm_property *property,
uint64_t *val)
{
struct drm_device *drm = connector->dev;
struct vc4_hdmi *vc4_hdmi =
connector_to_vc4_hdmi(connector);
const struct vc4_hdmi_connector_state *vc4_conn_state =
conn_state_to_vc4_hdmi_conn_state(state);
if (property == vc4_hdmi->broadcast_rgb_property) {
*val = vc4_conn_state->broadcast_rgb;
} else {
drm_dbg(drm, "Unknown property [PROP:%d:%s]\n",
property->base.id, property->name);
return -EINVAL;
}
return 0;
}
static int vc4_hdmi_connector_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
uint64_t val)
{
struct drm_device *drm = connector->dev;
struct vc4_hdmi *vc4_hdmi =
connector_to_vc4_hdmi(connector);
struct vc4_hdmi_connector_state *vc4_conn_state =
conn_state_to_vc4_hdmi_conn_state(state);
if (property == vc4_hdmi->broadcast_rgb_property) {
vc4_conn_state->broadcast_rgb = val;
return 0;
}
drm_dbg(drm, "Unknown property [PROP:%d:%s]\n",
property->base.id, property->name);
return -EINVAL;
} }
static void vc4_hdmi_connector_reset(struct drm_connector *connector) static void vc4_hdmi_connector_reset(struct drm_connector *connector)
{ {
struct vc4_hdmi_connector_state *old_state = drm_atomic_helper_connector_reset(connector);
conn_state_to_vc4_hdmi_conn_state(connector->state); __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
struct vc4_hdmi_connector_state *new_state =
kzalloc(sizeof(*new_state), GFP_KERNEL);
if (connector->state)
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(old_state);
__drm_atomic_helper_connector_reset(connector, &new_state->base);
if (!new_state)
return;
new_state->base.max_bpc = 8;
new_state->base.max_requested_bpc = 8;
new_state->output_format = VC4_HDMI_OUTPUT_RGB;
new_state->broadcast_rgb = VC4_HDMI_BROADCAST_RGB_AUTO;
drm_atomic_helper_connector_tv_margins_reset(connector); drm_atomic_helper_connector_tv_margins_reset(connector);
} }
static struct drm_connector_state *
vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
{
struct drm_connector_state *conn_state = connector->state;
struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
struct vc4_hdmi_connector_state *new_state;
new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
if (!new_state)
return NULL;
new_state->tmds_char_rate = vc4_state->tmds_char_rate;
new_state->output_bpc = vc4_state->output_bpc;
new_state->output_format = vc4_state->output_format;
new_state->broadcast_rgb = vc4_state->broadcast_rgb;
__drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
return &new_state->base;
}
static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
struct vc4_hdmi_connector_state *vc4_state =
conn_state_to_vc4_hdmi_conn_state(state);
__drm_atomic_helper_connector_destroy_state(state);
kfree(vc4_state);
}
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.reset = vc4_hdmi_connector_reset, .reset = vc4_hdmi_connector_reset,
.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = vc4_hdmi_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_get_property = vc4_hdmi_connector_get_property,
.atomic_set_property = vc4_hdmi_connector_set_property,
}; };
static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
...@@ -702,44 +572,29 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = ...@@ -702,44 +572,29 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
.atomic_check = vc4_hdmi_connector_atomic_check, .atomic_check = vc4_hdmi_connector_atomic_check,
}; };
static const struct drm_prop_enum_list broadcast_rgb_names[] = { static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs;
{ VC4_HDMI_BROADCAST_RGB_AUTO, "Automatic" },
{ VC4_HDMI_BROADCAST_RGB_FULL, "Full" },
{ VC4_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" },
};
static void
vc4_hdmi_attach_broadcast_rgb_property(struct drm_device *dev,
struct vc4_hdmi *vc4_hdmi)
{
struct drm_property *prop = vc4_hdmi->broadcast_rgb_property;
if (!prop) {
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
"Broadcast RGB",
broadcast_rgb_names,
ARRAY_SIZE(broadcast_rgb_names));
if (!prop)
return;
vc4_hdmi->broadcast_rgb_property = prop;
}
drm_object_attach_property(&vc4_hdmi->connector.base, prop,
VC4_HDMI_BROADCAST_RGB_AUTO);
}
static int vc4_hdmi_connector_init(struct drm_device *dev, static int vc4_hdmi_connector_init(struct drm_device *dev,
struct vc4_hdmi *vc4_hdmi) struct vc4_hdmi *vc4_hdmi)
{ {
struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_encoder *encoder = &vc4_hdmi->encoder.base; struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
unsigned int max_bpc = 8;
int ret; int ret;
ret = drmm_connector_init(dev, connector, if (vc4_hdmi->variant->supports_hdr)
max_bpc = 12;
ret = drmm_connector_hdmi_init(dev, connector,
"Broadcom", "Videocore",
&vc4_hdmi_connector_funcs, &vc4_hdmi_connector_funcs,
&vc4_hdmi_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA, DRM_MODE_CONNECTOR_HDMIA,
vc4_hdmi->ddc); vc4_hdmi->ddc,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
max_bpc);
if (ret) if (ret)
return ret; return ret;
...@@ -763,7 +618,6 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, ...@@ -763,7 +618,6 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
drm_connector_attach_colorspace_property(connector); drm_connector_attach_colorspace_property(connector);
drm_connector_attach_tv_margin_properties(connector); drm_connector_attach_tv_margin_properties(connector);
drm_connector_attach_max_bpc_property(connector, 8, 12);
connector->polled = (DRM_CONNECTOR_POLL_CONNECT | connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT); DRM_CONNECTOR_POLL_DISCONNECT);
...@@ -772,21 +626,19 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, ...@@ -772,21 +626,19 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
connector->stereo_allowed = 1; connector->stereo_allowed = 1;
if (vc4_hdmi->variant->supports_hdr) ret = drm_connector_attach_broadcast_rgb_property(connector);
drm_connector_attach_hdr_output_metadata_property(connector); if (ret)
return ret;
vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi);
drm_connector_attach_encoder(connector, encoder); drm_connector_attach_encoder(connector, encoder);
return 0; return 0;
} }
static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, static int vc4_hdmi_stop_packet(struct vc4_hdmi *vc4_hdmi,
enum hdmi_infoframe_type type, enum hdmi_infoframe_type type,
bool poll) bool poll)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = vc4_hdmi->connector.dev;
u32 packet_id = type - 0x80; u32 packet_id = type - 0x80;
unsigned long flags; unsigned long flags;
...@@ -810,12 +662,13 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, ...@@ -810,12 +662,13 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
return ret; return ret;
} }
static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, static int vc4_hdmi_write_infoframe(struct drm_connector *connector,
union hdmi_infoframe *frame) enum hdmi_infoframe_type type,
const u8 *infoframe, size_t len)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = connector->dev;
u32 packet_id = frame->any.type - 0x80; u32 packet_id = type - 0x80;
const struct vc4_hdmi_register *ram_packet_start = const struct vc4_hdmi_register *ram_packet_start =
&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id; u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
...@@ -825,22 +678,25 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, ...@@ -825,22 +678,25 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
ram_packet_start->reg); ram_packet_start->reg);
uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {}; uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {};
unsigned long flags; unsigned long flags;
ssize_t len, i; ssize_t i;
int ret; int ret;
int idx; int idx;
if (!drm_dev_enter(drm, &idx)) if (!drm_dev_enter(drm, &idx))
return; return 0;
if (len > sizeof(buffer)) {
ret = -ENOMEM;
goto out;
}
memcpy(buffer, infoframe, len);
WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
VC4_HDMI_RAM_PACKET_ENABLE), VC4_HDMI_RAM_PACKET_ENABLE),
"Packet RAM has to be on to store the packet."); "Packet RAM has to be on to store the packet.");
len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); ret = vc4_hdmi_stop_packet(vc4_hdmi, type, true);
if (len < 0)
goto out;
ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true);
if (ret) { if (ret) {
DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret); DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
goto out; goto out;
...@@ -882,130 +738,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, ...@@ -882,130 +738,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
out: out:
drm_dev_exit(idx); drm_dev_exit(idx);
} return ret;
static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
enum vc4_hdmi_output_format fmt)
{
switch (fmt) {
case VC4_HDMI_OUTPUT_RGB:
frame->colorspace = HDMI_COLORSPACE_RGB;
break;
case VC4_HDMI_OUTPUT_YUV420:
frame->colorspace = HDMI_COLORSPACE_YUV420;
break;
case VC4_HDMI_OUTPUT_YUV422:
frame->colorspace = HDMI_COLORSPACE_YUV422;
break;
case VC4_HDMI_OUTPUT_YUV444:
frame->colorspace = HDMI_COLORSPACE_YUV444;
break;
default:
break;
}
}
static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *cstate = connector->state;
struct vc4_hdmi_connector_state *vc4_state =
conn_state_to_vc4_hdmi_conn_state(cstate);
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
union hdmi_infoframe frame;
int ret;
lockdep_assert_held(&vc4_hdmi->mutex);
ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
connector, mode);
if (ret < 0) {
DRM_ERROR("couldn't fill AVI infoframe\n");
return;
}
drm_hdmi_avi_infoframe_quant_range(&frame.avi,
connector, mode,
vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ?
HDMI_QUANTIZATION_RANGE_FULL :
HDMI_QUANTIZATION_RANGE_LIMITED);
drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate);
vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format);
drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
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_audio_infoframe(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct hdmi_audio_infoframe *audio = &vc4_hdmi->audio.infoframe;
union hdmi_infoframe frame;
memcpy(&frame.audio, audio, sizeof(*audio));
if (vc4_hdmi->packet_ram_enabled)
vc4_hdmi_write_infoframe(encoder, &frame);
}
static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *conn_state = connector->state;
union hdmi_infoframe frame;
lockdep_assert_held(&vc4_hdmi->mutex);
if (!vc4_hdmi->variant->supports_hdr)
return;
if (!conn_state->hdr_output_metadata)
return;
if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state))
return;
vc4_hdmi_write_infoframe(encoder, &frame);
}
static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
lockdep_assert_held(&vc4_hdmi->mutex);
vc4_hdmi_set_avi_infoframe(encoder);
vc4_hdmi_set_spd_infoframe(encoder);
/*
* If audio was streaming, then we need to reenabled the audio
* infoframe here during encoder_enable.
*/
if (vc4_hdmi->audio.streaming)
vc4_hdmi_set_audio_infoframe(encoder);
vc4_hdmi_set_hdr_infoframe(encoder);
} }
#define SCRAMBLING_POLLING_DELAY_MS 1000 #define SCRAMBLING_POLLING_DELAY_MS 1000
...@@ -1174,8 +907,6 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, ...@@ -1174,8 +907,6 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
struct drm_connector_state *state, struct drm_connector_state *state,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct vc4_hdmi_connector_state *vc4_state =
conn_state_to_vc4_hdmi_conn_state(state);
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = vc4_hdmi->connector.dev;
unsigned long flags; unsigned long flags;
u32 csc_ctl; u32 csc_ctl;
...@@ -1189,7 +920,7 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, ...@@ -1189,7 +920,7 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
VC4_HD_CSC_CTL_ORDER); VC4_HD_CSC_CTL_ORDER);
if (!vc4_hdmi_is_full_range(vc4_hdmi, vc4_state)) { if (state->hdmi.is_limited_range) {
/* CEA VICs other than #1 requre limited range RGB /* CEA VICs other than #1 requre limited range RGB
* output unless overridden by an AVI infoframe. * output unless overridden by an AVI infoframe.
* Apply a colorspace conversion to squash 0-255 down * Apply a colorspace conversion to squash 0-255 down
...@@ -1412,9 +1143,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, ...@@ -1412,9 +1143,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = vc4_hdmi->connector.dev;
struct vc4_hdmi_connector_state *vc4_state = unsigned int lim_range = state->hdmi.is_limited_range ? 1 : 0;
conn_state_to_vc4_hdmi_conn_state(state);
unsigned int lim_range = vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? 0 : 1;
unsigned long flags; unsigned long flags;
const u16 (*csc)[4]; const u16 (*csc)[4];
u32 if_cfg = 0; u32 if_cfg = 0;
...@@ -1429,14 +1158,14 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, ...@@ -1429,14 +1158,14 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
switch (vc4_state->output_format) { switch (state->hdmi.output_format) {
case VC4_HDMI_OUTPUT_YUV444: case HDMI_COLORSPACE_YUV444:
csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range); csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range);
vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, csc); vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, csc);
break; break;
case VC4_HDMI_OUTPUT_YUV422: case HDMI_COLORSPACE_YUV422:
csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range); csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range);
csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
...@@ -1453,7 +1182,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, ...@@ -1453,7 +1182,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
vc5_hdmi_set_csc_coeffs(vc4_hdmi, csc); vc5_hdmi_set_csc_coeffs(vc4_hdmi, csc);
break; break;
case VC4_HDMI_OUTPUT_RGB: case HDMI_COLORSPACE_RGB:
if_xbar = 0x354021; if_xbar = 0x354021;
vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_rgb[lim_range]); vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_rgb[lim_range]);
...@@ -1542,8 +1271,6 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, ...@@ -1542,8 +1271,6 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = vc4_hdmi->connector.dev;
const struct vc4_hdmi_connector_state *vc4_state =
conn_state_to_vc4_hdmi_conn_state(state);
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
...@@ -1595,7 +1322,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, ...@@ -1595,7 +1322,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
HDMI_WRITE(HDMI_VERTB0, vertb_even); HDMI_WRITE(HDMI_VERTB0, vertb_even);
HDMI_WRITE(HDMI_VERTB1, vertb); HDMI_WRITE(HDMI_VERTB1, vertb);
switch (vc4_state->output_bpc) { switch (state->hdmi.output_bpc) {
case 12: case 12:
gcp = 6; gcp = 6;
break; break;
...@@ -1612,7 +1339,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, ...@@ -1612,7 +1339,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
* YCC422 is always 36-bit and not considered deep colour so * YCC422 is always 36-bit and not considered deep colour so
* doesn't signal in GCP. * doesn't signal in GCP.
*/ */
if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) { if (state->hdmi.output_format == HDMI_COLORSPACE_YUV422) {
gcp = 0; gcp = 0;
} }
...@@ -1696,10 +1423,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, ...@@ -1696,10 +1423,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *conn_state = struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector); drm_atomic_get_new_connector_state(state, connector);
struct vc4_hdmi_connector_state *vc4_conn_state =
conn_state_to_vc4_hdmi_conn_state(conn_state);
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate; unsigned long long tmds_char_rate = conn_state->hdmi.tmds_char_rate;
unsigned long bvb_rate, hsm_rate; unsigned long bvb_rate, hsm_rate;
unsigned long flags; unsigned long flags;
int ret; int ret;
...@@ -1734,7 +1459,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, ...@@ -1734,7 +1459,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
*/ */
hsm_rate = max_t(unsigned long, hsm_rate = max_t(unsigned long,
HSM_MIN_CLOCK_FREQ, HSM_MIN_CLOCK_FREQ,
(tmds_char_rate / 100) * 101); div_u64(tmds_char_rate, 100) * 101);
ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate); ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
if (ret) { if (ret) {
DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
...@@ -1776,7 +1501,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, ...@@ -1776,7 +1501,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
} }
if (vc4_hdmi->variant->phy_init) if (vc4_hdmi->variant->phy_init)
vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state); vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
...@@ -1841,7 +1566,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, ...@@ -1841,7 +1566,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state) struct drm_atomic_state *state)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_device *drm = connector->dev;
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
struct drm_display_info *display = &vc4_hdmi->connector.display_info; struct drm_display_info *display = &vc4_hdmi->connector.display_info;
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
...@@ -1907,7 +1633,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, ...@@ -1907,7 +1633,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
vc4_hdmi->packet_ram_enabled = true; vc4_hdmi->packet_ram_enabled = true;
vc4_hdmi_set_infoframes(encoder); drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
} }
vc4_hdmi_recenter_fifo(vc4_hdmi); vc4_hdmi_recenter_fifo(vc4_hdmi);
...@@ -1924,108 +1650,21 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, ...@@ -1924,108 +1650,21 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct vc4_hdmi_connector_state *vc4_state =
conn_state_to_vc4_hdmi_conn_state(conn_state);
mutex_lock(&vc4_hdmi->mutex); mutex_lock(&vc4_hdmi->mutex);
drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
&crtc_state->adjusted_mode); &crtc_state->adjusted_mode);
vc4_hdmi->output_bpc = vc4_state->output_bpc; vc4_hdmi->output_bpc = conn_state->hdmi.output_bpc;
vc4_hdmi->output_format = vc4_state->output_format; vc4_hdmi->output_format = conn_state->hdmi.output_format;
mutex_unlock(&vc4_hdmi->mutex); mutex_unlock(&vc4_hdmi->mutex);
} }
static bool
vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi,
const struct drm_display_info *info,
const struct drm_display_mode *mode,
unsigned int format, unsigned int bpc)
{
struct drm_device *dev = vc4_hdmi->connector.dev;
u8 vic = drm_match_cea_mode(mode);
if (vic == 1 && bpc != 8) {
drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
return false;
}
if (!info->is_hdmi &&
(format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) {
drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
return false;
}
switch (format) {
case VC4_HDMI_OUTPUT_RGB:
drm_dbg(dev, "RGB Format, checking the constraints.\n");
if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
return false;
if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
return false;
}
if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
return false;
}
drm_dbg(dev, "RGB format supported in that configuration.\n");
return true;
case VC4_HDMI_OUTPUT_YUV422:
drm_dbg(dev, "YUV422 format, checking the constraints.\n");
if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
drm_dbg(dev, "Sink doesn't support YUV422.\n");
return false;
}
if (bpc != 12) {
drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
return false;
}
drm_dbg(dev, "YUV422 format supported in that configuration.\n");
return true;
case VC4_HDMI_OUTPUT_YUV444:
drm_dbg(dev, "YUV444 format, checking the constraints.\n");
if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
drm_dbg(dev, "Sink doesn't support YUV444.\n");
return false;
}
if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
return false;
}
if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
return false;
}
drm_dbg(dev, "YUV444 format supported in that configuration.\n");
return true;
}
return false;
}
static enum drm_mode_status static enum drm_mode_status
vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
unsigned long long clock) unsigned long long clock)
{ {
const struct drm_connector *connector = &vc4_hdmi->connector; const struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
const struct drm_display_info *info = &connector->display_info;
struct vc4_dev *vc4 = to_vc4_dev(connector->dev); struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
if (clock > vc4_hdmi->variant->max_pixel_clock) if (clock > vc4_hdmi->variant->max_pixel_clock)
...@@ -2040,125 +1679,13 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, ...@@ -2040,125 +1679,13 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
drm_mode_vrefresh(mode) >= 50) drm_mode_vrefresh(mode) >= 50)
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
return MODE_CLOCK_HIGH;
return MODE_OK; return MODE_OK;
} }
static unsigned long long static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = {
vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid,
unsigned int bpc, .write_infoframe = vc4_hdmi_write_infoframe,
enum vc4_hdmi_output_format fmt) };
{
unsigned long long clock = mode->clock * 1000ULL;
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
clock = clock * 2;
if (fmt == VC4_HDMI_OUTPUT_YUV422)
bpc = 8;
clock = clock * bpc;
do_div(clock, 8);
return clock;
}
static int
vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_state,
const struct drm_display_mode *mode,
unsigned int bpc, unsigned int fmt)
{
unsigned long long clock;
clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK)
return -EINVAL;
vc4_state->tmds_char_rate = clock;
return 0;
}
static int
vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_state,
const struct drm_display_mode *mode,
unsigned int bpc)
{
struct drm_device *dev = vc4_hdmi->connector.dev;
const struct drm_connector *connector = &vc4_hdmi->connector;
const struct drm_display_info *info = &connector->display_info;
unsigned int format;
drm_dbg(dev, "Trying with an RGB output\n");
format = VC4_HDMI_OUTPUT_RGB;
if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
int ret;
ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
mode, bpc, format);
if (!ret) {
vc4_state->output_format = format;
return 0;
}
}
drm_dbg(dev, "Failed, Trying with an YUV422 output\n");
format = VC4_HDMI_OUTPUT_YUV422;
if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
int ret;
ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
mode, bpc, format);
if (!ret) {
vc4_state->output_format = format;
return 0;
}
}
drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
return -EINVAL;
}
static int
vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_state,
const struct drm_display_mode *mode)
{
struct drm_device *dev = vc4_hdmi->connector.dev;
struct drm_connector_state *conn_state = &vc4_state->base;
unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12);
unsigned int bpc;
int ret;
for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state,
mode, bpc);
if (ret)
continue;
vc4_state->output_bpc = bpc;
drm_dbg(dev,
"Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
vc4_state->output_bpc,
vc4_hdmi_output_fmt_str(vc4_state->output_format),
vc4_state->tmds_char_rate);
break;
}
return ret;
}
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
#define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL
...@@ -2168,16 +1695,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -2168,16 +1695,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *old_conn_state =
drm_atomic_get_old_connector_state(conn_state->state, connector);
struct vc4_hdmi_connector_state *old_vc4_state =
conn_state_to_vc4_hdmi_conn_state(old_conn_state);
struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct drm_display_mode *mode = &crtc_state->adjusted_mode;
unsigned long long tmds_char_rate = mode->clock * 1000; unsigned long long tmds_char_rate = mode->clock * 1000;
unsigned long long tmds_bit_rate; unsigned long long tmds_bit_rate;
int ret;
if (vc4_hdmi->variant->unsupported_odd_h_timings) { if (vc4_hdmi->variant->unsupported_odd_h_timings) {
if (mode->flags & DRM_MODE_FLAG_DBLCLK) { if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
...@@ -2213,15 +1733,6 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -2213,15 +1733,6 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
tmds_char_rate = mode->clock * 1000; tmds_char_rate = mode->clock * 1000;
} }
ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode);
if (ret)
return ret;
/* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */
if (vc4_state->output_bpc != old_vc4_state->output_bpc ||
vc4_state->output_format != old_vc4_state->output_format)
crtc_state->mode_changed = true;
return 0; return 0;
} }
...@@ -2230,6 +1741,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, ...@@ -2230,6 +1741,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
unsigned long long rate;
if (vc4_hdmi->variant->unsupported_odd_h_timings && if (vc4_hdmi->variant->unsupported_odd_h_timings &&
!(mode->flags & DRM_MODE_FLAG_DBLCLK) && !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
...@@ -2237,7 +1749,8 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, ...@@ -2237,7 +1749,8 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
(mode->hsync_end % 2) || (mode->htotal % 2))) (mode->hsync_end % 2) || (mode->htotal % 2)))
return MODE_H_ILLEGAL; return MODE_H_ILLEGAL;
return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, mode->clock * 1000); rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
return vc4_hdmi_connector_clock_valid(&vc4_hdmi->connector, mode, rate);
} }
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
...@@ -2429,7 +1942,6 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data) ...@@ -2429,7 +1942,6 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data)
static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
{ {
struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
struct device *dev = &vc4_hdmi->pdev->dev; struct device *dev = &vc4_hdmi->pdev->dev;
unsigned long flags; unsigned long flags;
int ret; int ret;
...@@ -2437,7 +1949,7 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) ...@@ -2437,7 +1949,7 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
lockdep_assert_held(&vc4_hdmi->mutex); lockdep_assert_held(&vc4_hdmi->mutex);
vc4_hdmi->audio.streaming = false; vc4_hdmi->audio.streaming = false;
ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false); ret = vc4_hdmi_stop_packet(vc4_hdmi, HDMI_INFOFRAME_TYPE_AUDIO, false);
if (ret) if (ret)
dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
...@@ -2528,7 +2040,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, ...@@ -2528,7 +2040,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
{ {
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_device *drm = vc4_hdmi->connector.dev;
struct drm_encoder *encoder = &vc4_hdmi->encoder.base; struct drm_connector *connector = &vc4_hdmi->connector;
unsigned int sample_rate = params->sample_rate; unsigned int sample_rate = params->sample_rate;
unsigned int channels = params->channels; unsigned int channels = params->channels;
unsigned long flags; unsigned long flags;
...@@ -2605,8 +2117,10 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, ...@@ -2605,8 +2117,10 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea)); ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
vc4_hdmi_set_audio_infoframe(encoder); &params->cea);
if (ret)
goto out_dev_exit;
out_dev_exit: out_dev_exit:
drm_dev_exit(idx); drm_dev_exit(idx);
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
struct vc4_hdmi; struct vc4_hdmi;
struct vc4_hdmi_register; struct vc4_hdmi_register;
struct vc4_hdmi_connector_state;
enum vc4_hdmi_phy_channel { enum vc4_hdmi_phy_channel {
PHY_LANE_0 = 0, PHY_LANE_0 = 0,
...@@ -76,7 +75,7 @@ struct vc4_hdmi_variant { ...@@ -76,7 +75,7 @@ struct vc4_hdmi_variant {
/* Callback to initialize the PHY according to the connector state */ /* Callback to initialize the PHY according to the connector state */
void (*phy_init)(struct vc4_hdmi *vc4_hdmi, void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_conn_state); struct drm_connector_state *conn_state);
/* Callback to disable the PHY */ /* Callback to disable the PHY */
void (*phy_disable)(struct vc4_hdmi *vc4_hdmi); void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
...@@ -110,19 +109,6 @@ struct vc4_hdmi_audio { ...@@ -110,19 +109,6 @@ struct vc4_hdmi_audio {
bool streaming; bool streaming;
}; };
enum vc4_hdmi_output_format {
VC4_HDMI_OUTPUT_RGB,
VC4_HDMI_OUTPUT_YUV422,
VC4_HDMI_OUTPUT_YUV444,
VC4_HDMI_OUTPUT_YUV420,
};
enum vc4_hdmi_broadcast_rgb {
VC4_HDMI_BROADCAST_RGB_AUTO,
VC4_HDMI_BROADCAST_RGB_FULL,
VC4_HDMI_BROADCAST_RGB_LIMITED,
};
/* General HDMI hardware state. */ /* General HDMI hardware state. */
struct vc4_hdmi { struct vc4_hdmi {
struct vc4_hdmi_audio audio; struct vc4_hdmi_audio audio;
...@@ -135,8 +121,6 @@ struct vc4_hdmi { ...@@ -135,8 +121,6 @@ struct vc4_hdmi {
struct delayed_work scrambling_work; struct delayed_work scrambling_work;
struct drm_property *broadcast_rgb_property;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
void __iomem *hdmicore_regs; void __iomem *hdmicore_regs;
void __iomem *hd_regs; void __iomem *hd_regs;
...@@ -218,16 +202,17 @@ struct vc4_hdmi { ...@@ -218,16 +202,17 @@ struct vc4_hdmi {
bool scdc_enabled; bool scdc_enabled;
/** /**
* @output_bpc: Copy of @vc4_connector_state.output_bpc for use * @output_bpc: Copy of @drm_connector_state.hdmi.output_bpc for
* outside of KMS hooks. Protected by @mutex. * use outside of KMS hooks. Protected by @mutex.
*/ */
unsigned int output_bpc; unsigned int output_bpc;
/** /**
* @output_format: Copy of @vc4_connector_state.output_format * @output_format: Copy of
* for use outside of KMS hooks. Protected by @mutex. * @drm_connector_state.hdmi.output_format for use outside of
* KMS hooks. Protected by @mutex.
*/ */
enum vc4_hdmi_output_format output_format; enum hdmi_colorspace output_format;
}; };
#define connector_to_vc4_hdmi(_connector) \ #define connector_to_vc4_hdmi(_connector) \
...@@ -240,25 +225,14 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder) ...@@ -240,25 +225,14 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder)
return container_of_const(_encoder, struct vc4_hdmi, encoder); return container_of_const(_encoder, struct vc4_hdmi, encoder);
} }
struct vc4_hdmi_connector_state {
struct drm_connector_state base;
unsigned long long tmds_char_rate;
unsigned int output_bpc;
enum vc4_hdmi_output_format output_format;
enum vc4_hdmi_broadcast_rgb broadcast_rgb;
};
#define conn_state_to_vc4_hdmi_conn_state(_state) \
container_of_const(_state, struct vc4_hdmi_connector_state, base)
void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_conn_state); struct drm_connector_state *conn_state);
void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *vc4_conn_state); struct drm_connector_state *conn_state);
void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
......
...@@ -128,7 +128,7 @@ ...@@ -128,7 +128,7 @@
#define OSCILLATOR_FREQUENCY 54000000 #define OSCILLATOR_FREQUENCY 54000000
void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
unsigned long flags; unsigned long flags;
...@@ -361,11 +361,11 @@ static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi) ...@@ -361,11 +361,11 @@ static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
} }
void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
struct vc4_hdmi_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings; const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings;
const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
unsigned long long pixel_freq = conn_state->tmds_char_rate; unsigned long long pixel_freq = conn_state->hdmi.tmds_char_rate;
unsigned long long vco_freq; unsigned long long vco_freq;
unsigned char word_sel; unsigned char word_sel;
unsigned long flags; unsigned long flags;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment