summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c201
1 files changed, 103 insertions, 98 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 152375f3de2e..ab0938ba61f7 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -16,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
@@ -25,64 +26,42 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+
#include "sun4i_backend.h"
#include "sun4i_crtc.h"
#include "sun4i_drv.h"
#include "sun4i_hdmi.h"
-static inline struct sun4i_hdmi *
-drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
-{
- return container_of(encoder, struct sun4i_hdmi,
- encoder);
-}
+#define drm_encoder_to_sun4i_hdmi(e) \
+ container_of_const(e, struct sun4i_hdmi, encoder)
-static inline struct sun4i_hdmi *
-drm_connector_to_sun4i_hdmi(struct drm_connector *connector)
-{
- return container_of(connector, struct sun4i_hdmi,
- connector);
-}
+#define drm_connector_to_sun4i_hdmi(c) \
+ container_of_const(c, struct sun4i_hdmi, connector)
-static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
- struct drm_display_mode *mode)
+static int sun4i_hdmi_write_infoframe(struct drm_connector *connector,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len)
{
- struct hdmi_avi_infoframe frame;
- u8 buffer[17];
- int i, ret;
-
- ret = drm_hdmi_avi_infoframe_from_display_mode(&frame,
- &hdmi->connector, mode);
- if (ret < 0) {
- DRM_ERROR("Failed to get infoframes from mode\n");
- return ret;
- }
+ struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
+ int i;
- ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
- if (ret < 0) {
- DRM_ERROR("Failed to pack infoframes\n");
- return ret;
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", type);
+ return 0;
}
- for (i = 0; i < sizeof(buffer); i++)
+ for (i = 0; i < len; i++)
writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i));
return 0;
-}
-static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_display_mode *mode = &crtc_state->mode;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- return -EINVAL;
-
- return 0;
}
-static void sun4i_hdmi_disable(struct drm_encoder *encoder)
+static void sun4i_hdmi_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
u32 val;
@@ -96,39 +75,23 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder)
clk_disable_unprepare(hdmi->tmds_clk);
}
-static void sun4i_hdmi_enable(struct drm_encoder *encoder)
+static void sun4i_hdmi_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- struct drm_display_info *display = &hdmi->connector.display_info;
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_display_info *display = &connector->display_info;
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ unsigned long long tmds_rate = conn_state->hdmi.tmds_char_rate;
+ unsigned int x, y;
u32 val = 0;
DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
- clk_prepare_enable(hdmi->tmds_clk);
-
- sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
- writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
-
- val = SUN4I_HDMI_VID_CTRL_ENABLE;
- if (display->is_hdmi)
- val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
-
- writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
-}
-
-static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned int x, y;
- u32 val;
-
- clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
- clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
+ clk_set_rate(hdmi->mod_clk, tmds_rate);
+ clk_set_rate(hdmi->tmds_clk, tmds_rate);
/* Set input sync enable */
writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC,
@@ -178,52 +141,73 @@ static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
+
+ clk_prepare_enable(hdmi->tmds_clk);
+
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
+ writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
+
+ val = SUN4I_HDMI_VID_CTRL_ENABLE;
+ if (display->is_hdmi)
+ val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
+
+ writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
}
-static enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder,
- const struct drm_display_mode *mode)
+static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
+ .atomic_disable = sun4i_hdmi_disable,
+ .atomic_enable = sun4i_hdmi_enable,
+};
+
+static enum drm_mode_status
+sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long clock)
{
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned long rate = mode->clock * 1000;
- unsigned long diff = rate / 200; /* +-0.5% allowed by HDMI spec */
+ const struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
+ unsigned long diff = div_u64(clock, 200); /* +-0.5% allowed by HDMI spec */
long rounded_rate;
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
/* 165 MHz is the typical max pixelclock frequency for HDMI <= 1.2 */
- if (rate > 165000000)
+ if (clock > 165000000)
return MODE_CLOCK_HIGH;
- rounded_rate = clk_round_rate(hdmi->tmds_clk, rate);
+
+ rounded_rate = clk_round_rate(hdmi->tmds_clk, clock);
if (rounded_rate > 0 &&
- max_t(unsigned long, rounded_rate, rate) -
- min_t(unsigned long, rounded_rate, rate) < diff)
+ max_t(unsigned long, rounded_rate, clock) -
+ min_t(unsigned long, rounded_rate, clock) < diff)
return MODE_OK;
+
return MODE_NOCLOCK;
}
-static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
- .atomic_check = sun4i_hdmi_atomic_check,
- .disable = sun4i_hdmi_disable,
- .enable = sun4i_hdmi_enable,
- .mode_set = sun4i_hdmi_mode_set,
- .mode_valid = sun4i_hdmi_mode_valid,
-};
-
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
{
struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
- struct edid *edid;
+ const struct drm_edid *drm_edid;
int ret;
- edid = drm_get_edid(connector, hdmi->ddc_i2c ?: hdmi->i2c);
- if (!edid)
+ drm_edid = drm_edid_read_ddc(connector, hdmi->ddc_i2c ?: hdmi->i2c);
+
+ drm_edid_connector_update(connector, drm_edid);
+ cec_s_phys_addr(hdmi->cec_adap,
+ connector->display_info.source_physical_address, false);
+
+ if (!drm_edid)
return 0;
DRM_DEBUG_DRIVER("Monitor is %s monitor\n",
connector->display_info.is_hdmi ? "an HDMI" : "a DVI");
- drm_connector_update_edid_property(connector, edid);
- cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
+
+ ret = drm_edid_connector_add_modes(connector);
+ drm_edid_free(drm_edid);
return ret;
}
@@ -250,7 +234,14 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
return ddc;
}
+static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = {
+ .tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid,
+ .write_infoframe = sun4i_hdmi_write_infoframe,
+};
+
static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
+ .atomic_check = drm_atomic_helper_connector_hdmi_check,
+ .mode_valid = drm_hdmi_connector_mode_valid,
.get_modes = sun4i_hdmi_get_modes,
};
@@ -269,11 +260,16 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
return connector_status_connected;
}
+static void sun4i_hdmi_connector_reset(struct drm_connector *connector)
+{
+ drm_atomic_helper_connector_reset(connector);
+ __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
+}
+
static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
.detect = sun4i_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .reset = sun4i_hdmi_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@@ -632,10 +628,19 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
drm_connector_helper_add(&hdmi->connector,
&sun4i_hdmi_connector_helper_funcs);
- ret = drm_connector_init_with_ddc(drm, &hdmi->connector,
- &sun4i_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- hdmi->ddc_i2c);
+ ret = drmm_connector_hdmi_init(drm, &hdmi->connector,
+ /*
+ * NOTE: Those are likely to be
+ * wrong, but I couldn't find the
+ * actual ones in the BSP.
+ */
+ "AW", "HDMI",
+ &sun4i_hdmi_connector_funcs,
+ &sun4i_hdmi_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ hdmi->ddc_i2c,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
if (ret) {
dev_err(dev,
"Couldn't initialise the HDMI connector\n");
@@ -708,7 +713,7 @@ MODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table);
static struct platform_driver sun4i_hdmi_driver = {
.probe = sun4i_hdmi_probe,
- .remove_new = sun4i_hdmi_remove,
+ .remove = sun4i_hdmi_remove,
.driver = {
.name = "sun4i-hdmi",
.of_match_table = sun4i_hdmi_of_table,