mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
drm/rockchip: inno_hdmi: Add variant support
In preparation to support RK3128's integration of the controller, this patch adds a simple variant implementation. They mainly differ in the phy configuration required, so those are part of the match_data. The values have been taken from downstream. The pixelclocks in there are meant to be max-inclusive. Signed-off-by: Alex Bee <knaerzche@gmail.com> Signed-off-by: Heiko Stuebner <heiko@sntech.de> Link: https://patchwork.freedesktop.org/patch/msgid/20231222174220.55249-24-knaerzche@gmail.com
This commit is contained in:
parent
bb056046c2
commit
5f2e93e671
|
|
@ -25,6 +25,17 @@
|
|||
|
||||
#include "inno_hdmi.h"
|
||||
|
||||
struct inno_hdmi_phy_config {
|
||||
unsigned long pixelclock;
|
||||
u8 pre_emphasis;
|
||||
u8 voltage_level_control;
|
||||
};
|
||||
|
||||
struct inno_hdmi_variant {
|
||||
struct inno_hdmi_phy_config *phy_configs;
|
||||
struct inno_hdmi_phy_config *default_phy_config;
|
||||
};
|
||||
|
||||
struct inno_hdmi_i2c {
|
||||
struct i2c_adapter adap;
|
||||
|
||||
|
|
@ -46,6 +57,8 @@ struct inno_hdmi {
|
|||
|
||||
struct inno_hdmi_i2c *i2c;
|
||||
struct i2c_adapter *ddc;
|
||||
|
||||
const struct inno_hdmi_variant *variant;
|
||||
};
|
||||
|
||||
struct inno_hdmi_connector_state {
|
||||
|
|
@ -112,6 +125,30 @@ static const char coeff_csc[][24] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
|
||||
{ 74250000, 0x3f, 0xbb },
|
||||
{ 165000000, 0x6f, 0xbb },
|
||||
{ ~0UL, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
|
||||
unsigned long pixelclk)
|
||||
{
|
||||
const struct inno_hdmi_phy_config *phy_configs =
|
||||
hdmi->variant->phy_configs;
|
||||
int i;
|
||||
|
||||
for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) {
|
||||
if (pixelclk <= phy_configs[i].pixelclock)
|
||||
return i;
|
||||
}
|
||||
|
||||
DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %lu\n",
|
||||
pixelclk);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
|
||||
{
|
||||
return readl_relaxed(hdmi->regs + (offset) * 0x04);
|
||||
|
|
@ -163,12 +200,25 @@ static void inno_hdmi_standby(struct inno_hdmi *hdmi)
|
|||
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
|
||||
};
|
||||
|
||||
static void inno_hdmi_power_up(struct inno_hdmi *hdmi)
|
||||
static void inno_hdmi_power_up(struct inno_hdmi *hdmi,
|
||||
unsigned long mpixelclock)
|
||||
{
|
||||
struct inno_hdmi_phy_config *phy_config;
|
||||
int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock);
|
||||
|
||||
if (ret < 0) {
|
||||
phy_config = hdmi->variant->default_phy_config;
|
||||
DRM_DEV_ERROR(hdmi->dev,
|
||||
"Using default phy configuration for TMDS rate %lu",
|
||||
mpixelclock);
|
||||
} else {
|
||||
phy_config = &hdmi->variant->phy_configs[ret];
|
||||
}
|
||||
|
||||
inno_hdmi_sys_power(hdmi, false);
|
||||
|
||||
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
|
||||
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
|
||||
|
|
@ -405,6 +455,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
|||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_display_info *display = &hdmi->connector.display_info;
|
||||
unsigned long mpixelclock = mode->clock * 1000;
|
||||
|
||||
/* Mute video and audio output */
|
||||
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
|
||||
|
|
@ -427,13 +478,13 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
|||
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
|
||||
* clock rate, and reconfigure the DDC clock.
|
||||
*/
|
||||
inno_hdmi_i2c_init(hdmi, mode->clock * 1000);
|
||||
inno_hdmi_i2c_init(hdmi, mpixelclock);
|
||||
|
||||
/* Unmute video and audio output */
|
||||
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
|
||||
v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
|
||||
|
||||
inno_hdmi_power_up(hdmi);
|
||||
inno_hdmi_power_up(hdmi, mpixelclock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -825,6 +876,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
|
|||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct inno_hdmi *hdmi;
|
||||
const struct inno_hdmi_variant *variant;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
|
|
@ -834,6 +886,12 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
|
|||
|
||||
hdmi->dev = dev;
|
||||
|
||||
variant = of_device_get_match_data(hdmi->dev);
|
||||
if (!variant)
|
||||
return -EINVAL;
|
||||
|
||||
hdmi->variant = variant;
|
||||
|
||||
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hdmi->regs))
|
||||
return PTR_ERR(hdmi->regs);
|
||||
|
|
@ -927,8 +985,14 @@ static void inno_hdmi_remove(struct platform_device *pdev)
|
|||
component_del(&pdev->dev, &inno_hdmi_ops);
|
||||
}
|
||||
|
||||
static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
|
||||
.phy_configs = rk3036_hdmi_phy_configs,
|
||||
.default_phy_config = &rk3036_hdmi_phy_configs[1],
|
||||
};
|
||||
|
||||
static const struct of_device_id inno_hdmi_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3036-inno-hdmi",
|
||||
.data = &rk3036_inno_hdmi_variant,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user