drm/rockchip: rk3066_hdmi: switch to drm bridge

Convert it to drm bridge driver, it will be convenient for us to
migrate the connector part to the display driver later.

Note: I don't have the hardware to test this driver, so for now
I can only do the compilation test.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Tested-by: Heiko Stuebner <heiko@sntech.de> #rk3066-marsboard
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://lore.kernel.org/r/20250428102309.1501986-1-andyshrk@163.com
This commit is contained in:
Andy Yan 2025-04-28 18:23:07 +08:00 committed by Heiko Stuebner
parent e782ac9369
commit 57d6811e8a

View File

@ -5,6 +5,9 @@
*/
#include <drm/drm_atomic.h>
#include <drm/drm_bridge_connector.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
@ -46,27 +49,20 @@ struct rk3066_hdmi {
struct clk *hclk;
void __iomem *regs;
struct drm_connector connector;
struct drm_bridge bridge;
struct drm_connector *connector;
struct rockchip_encoder encoder;
struct rk3066_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
unsigned int tmdsclk;
struct hdmi_data_info hdmi_data;
};
static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
static struct rk3066_hdmi *bridge_to_rk3066_hdmi(struct drm_bridge *bridge)
{
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
return container_of(rkencoder, struct rk3066_hdmi, encoder);
}
static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
{
return container_of(connector, struct rk3066_hdmi, connector);
return container_of(bridge, struct rk3066_hdmi, bridge);
}
static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
@ -161,57 +157,40 @@ static void rk3066_hdmi_set_power_mode(struct rk3066_hdmi *hdmi, int mode)
hdmi->tmdsclk = DEFAULT_PLLA_RATE;
}
static int
rk3066_hdmi_upload_frame(struct rk3066_hdmi *hdmi, int setup_rc,
union hdmi_infoframe *frame, u32 frame_index,
u32 mask, u32 disable, u32 enable)
static int rk3066_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
{
if (mask)
hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, mask, disable);
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
hdmi_writeb(hdmi, HDMI_CP_BUF_INDEX, frame_index);
if (setup_rc >= 0) {
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;
rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
if (rc < 0)
return rc;
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CP_BUF_ACC_HB0 + i * 4,
packed_frame[i]);
if (mask)
hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, mask, enable);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
return setup_rc;
hdmi_writeb(hdmi, HDMI_CP_BUF_INDEX, HDMI_INFOFRAME_AVI);
return 0;
}
static int rk3066_hdmi_config_avi(struct rk3066_hdmi *hdmi,
struct drm_display_mode *mode)
static int
rk3066_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{
union hdmi_infoframe frame;
int rc;
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
ssize_t i;
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector, mode);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
return 0;
}
if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
rk3066_hdmi_bridge_clear_infoframe(bridge, type);
frame.avi.colorimetry = hdmi->hdmi_data.colorimetry;
frame.avi.scan_mode = HDMI_SCAN_MODE_NONE;
for (i = 0; i < len; i++)
hdmi_writeb(hdmi, HDMI_CP_BUF_ACC_HB0 + i * 4, buffer[i]);
return rk3066_hdmi_upload_frame(hdmi, rc, &frame,
HDMI_INFOFRAME_AVI, 0, 0, 0);
return 0;
}
static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi,
@ -324,9 +303,27 @@ static void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi)
}
static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi,
struct drm_display_mode *mode)
struct drm_atomic_state *state)
{
struct drm_display_info *display = &hdmi->connector.display_info;
struct drm_bridge *bridge = &hdmi->bridge;
struct drm_connector *connector;
struct drm_display_info *display;
struct drm_display_mode *mode;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *new_crtc_state;
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
new_conn_state = drm_atomic_get_new_connector_state(state, connector);
if (WARN_ON(!new_conn_state))
return -EINVAL;
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
if (WARN_ON(!new_crtc_state))
return -EINVAL;
display = &connector->display_info;
mode = &new_crtc_state->adjusted_mode;
hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
@ -363,7 +360,7 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi,
if (display->is_hdmi) {
hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK,
HDMI_VIDEO_MODE_HDMI);
rk3066_hdmi_config_avi(hdmi, mode);
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
} else {
hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, 0);
}
@ -386,15 +383,15 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi,
return 0;
}
static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
static void rk3066_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
int mux, val;
conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector);
conn_state = drm_atomic_get_new_connector_state(state, hdmi->connector);
if (WARN_ON(!conn_state))
return;
@ -402,7 +399,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder,
if (WARN_ON(!crtc_state))
return;
mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, &hdmi->encoder.encoder);
if (mux)
val = (HDMI_VIDEO_SEL << 16) | HDMI_VIDEO_SEL;
else
@ -413,13 +410,13 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder,
DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder enable select: vop%s\n",
(mux) ? "1" : "0");
rk3066_hdmi_setup(hdmi, &crtc_state->adjusted_mode);
rk3066_hdmi_setup(hdmi, state);
}
static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
static void rk3066_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
@ -450,39 +447,34 @@ rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
static const
struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
.atomic_check = rk3066_hdmi_encoder_atomic_check,
.atomic_enable = rk3066_hdmi_encoder_enable,
.atomic_disable = rk3066_hdmi_encoder_disable,
};
static enum drm_connector_status
rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
rk3066_hdmi_bridge_detect(struct drm_bridge *bridge)
{
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
connector_status_connected : connector_status_disconnected;
}
static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
static const struct drm_edid *
rk3066_hdmi_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector)
{
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
struct rk3066_hdmi *hdmi = bridge_to_rk3066_hdmi(bridge);
const struct drm_edid *drm_edid;
int ret = 0;
if (!hdmi->ddc)
return 0;
drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
if (!drm_edid)
dev_dbg(hdmi->dev, "failed to get edid\n");
drm_edid = drm_edid_read_ddc(connector, hdmi->ddc);
drm_edid_connector_update(connector, drm_edid);
ret = drm_edid_connector_add_modes(connector);
drm_edid_free(drm_edid);
return ret;
return drm_edid;
}
static enum drm_mode_status
rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
rk3066_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
u32 vic = drm_match_cea_mode(mode);
@ -492,82 +484,19 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
return MODE_BAD;
}
static struct drm_encoder *
rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
{
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
return &hdmi->encoder.encoder;
}
static int
rk3066_hdmi_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
{
if (maxX > 1920)
maxX = 1920;
if (maxY > 1080)
maxY = 1080;
return drm_helper_probe_single_connector_modes(connector, maxX, maxY);
}
static void rk3066_hdmi_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs rk3066_hdmi_connector_funcs = {
.fill_modes = rk3066_hdmi_probe_single_connector_modes,
.detect = rk3066_hdmi_connector_detect,
.destroy = rk3066_hdmi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
static const struct drm_bridge_funcs rk3066_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_enable = rk3066_hdmi_bridge_atomic_enable,
.atomic_disable = rk3066_hdmi_bridge_atomic_disable,
.detect = rk3066_hdmi_bridge_detect,
.edid_read = rk3066_hdmi_bridge_edid_read,
.hdmi_clear_infoframe = rk3066_hdmi_bridge_clear_infoframe,
.hdmi_write_infoframe = rk3066_hdmi_bridge_write_infoframe,
.mode_valid = rk3066_hdmi_bridge_mode_valid,
};
static const
struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
.get_modes = rk3066_hdmi_connector_get_modes,
.mode_valid = rk3066_hdmi_connector_mode_valid,
.best_encoder = rk3066_hdmi_connector_best_encoder,
};
static int
rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
{
struct drm_encoder *encoder = &hdmi->encoder.encoder;
struct device *dev = hdmi->dev;
encoder->possible_crtcs =
drm_of_find_possible_crtcs(drm, dev->of_node);
/*
* If we failed to find the CRTC(s) which this encoder is
* supposed to be connected to, it's because the CRTC has
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (encoder->possible_crtcs == 0)
return -EPROBE_DEFER;
drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
drm_connector_helper_add(&hdmi->connector,
&rk3066_hdmi_connector_helper_funcs);
drm_connector_init_with_ddc(drm, &hdmi->connector,
&rk3066_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
hdmi->ddc);
drm_connector_attach_encoder(&hdmi->connector, encoder);
return 0;
}
static irqreturn_t rk3066_hdmi_hardirq(int irq, void *dev_id)
{
@ -597,7 +526,7 @@ static irqreturn_t rk3066_hdmi_irq(int irq, void *dev_id)
{
struct rk3066_hdmi *hdmi = dev_id;
drm_helper_hpd_irq_event(hdmi->connector.dev);
drm_helper_hpd_irq_event(hdmi->connector->dev);
return IRQ_HANDLED;
}
@ -720,7 +649,7 @@ static struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi)
strscpy(adap->name, "RK3066 HDMI", sizeof(adap->name));
i2c_set_adapdata(adap, hdmi);
ret = i2c_add_adapter(adap);
ret = devm_i2c_add_adapter(hdmi->dev, adap);
if (ret) {
DRM_DEV_ERROR(hdmi->dev, "cannot add %s I2C adapter\n",
adap->name);
@ -735,6 +664,66 @@ static struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi)
return adap;
}
static int
rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
{
struct drm_encoder *encoder = &hdmi->encoder.encoder;
struct device *dev = hdmi->dev;
int ret;
encoder->possible_crtcs =
drm_of_find_possible_crtcs(drm, dev->of_node);
/*
* If we failed to find the CRTC(s) which this encoder is
* supposed to be connected to, it's because the CRTC has
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (encoder->possible_crtcs == 0)
return -EPROBE_DEFER;
drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = hdmi->dev->of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Rockchip";
hdmi->bridge.product = "RK3066 HDMI";
hdmi->bridge.ddc = rk3066_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))
return PTR_ERR(hdmi->bridge.ddc);
if (IS_ERR(hdmi->bridge.ddc))
return PTR_ERR(hdmi->bridge.ddc);
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
if (ret)
return ret;
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
hdmi->connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(hdmi->connector)) {
ret = PTR_ERR(hdmi->connector);
dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
return ret;
}
drm_connector_attach_encoder(hdmi->connector, encoder);
return 0;
}
static int rk3066_hdmi_bind(struct device *dev, struct device *master,
void *data)
{
@ -781,13 +770,6 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
/* internal hclk = hdmi_hclk / 25 */
hdmi_writeb(hdmi, HDMI_INTERNAL_CLK_DIVIDER, 25);
hdmi->ddc = rk3066_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->ddc)) {
ret = PTR_ERR(hdmi->ddc);
hdmi->ddc = NULL;
goto err_disable_hclk;
}
rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_B);
usleep_range(999, 1000);
hdmi_writeb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_HOTPLUG);
@ -798,7 +780,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
ret = rk3066_hdmi_register(drm, hdmi);
if (ret)
goto err_disable_i2c;
goto err_disable_hclk;
dev_set_drvdata(dev, hdmi);
@ -813,10 +795,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
return 0;
err_cleanup_hdmi:
hdmi->connector.funcs->destroy(&hdmi->connector);
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
err_disable_i2c:
i2c_put_adapter(hdmi->ddc);
err_disable_hclk:
clk_disable_unprepare(hdmi->hclk);
@ -828,10 +807,8 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
{
struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
hdmi->connector.funcs->destroy(&hdmi->connector);
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
i2c_put_adapter(hdmi->ddc);
clk_disable_unprepare(hdmi->hclk);
}