From 351e1354fcd06e96245f8fda141ac3cc2f68f763 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 31 Jan 2015 14:50:23 +0000 Subject: drm: bridge/dw_hdmi: combine hdmi_set_clock_regenerator_n() and hdmi_regenerate_cts() Combine these two functions into a single implementation. These two functions are called consecutively anyway. Idea from a patch by Yakir Yang. Acked-by: Philipp Zabel Tested-by: Philipp Zabel Acked-by: Andy Yan Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index cd6a70647e32..78363552d80e 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -177,19 +177,16 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); } -static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi, - unsigned int value) +static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, + unsigned int n) { - hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); - hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); - hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); + hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); + hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3); /* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); -} -static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) -{ /* Must be set/cleared first */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); @@ -355,8 +352,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, __func__, hdmi->sample_rate, hdmi->ratio, pixel_clk, clk_n, clk_cts); - hdmi_set_clock_regenerator_n(hdmi, clk_n); - hdmi_regenerate_cts(hdmi, clk_cts); + hdmi_set_cts_n(hdmi, clk_cts, clk_n); } static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) -- cgit v1.2.3 From 6bcf495317857e4e4cfb2e6f57fa8230cedc1362 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 2 Feb 2015 11:01:08 +0000 Subject: drm: bridge/dw_hdmi: protect n/cts setting with a mutex The HDMI n/cts settings need to be updated whenever the audio sample rate or the video pixel clock changes. This needs to be protected against concurrency as there is no synchronisation between these two operations. Introduce a mutex (called audio_mutex) to protect against two threads trying to update the video clock rate and pixel clock simultaneously. Acked-by: Philipp Zabel Tested-by: Philipp Zabel Acked-by: Andy Yan Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 78363552d80e..b75922d4901e 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,7 @@ struct dw_hdmi { struct i2c_adapter *ddc; void __iomem *regs; + struct mutex audio_mutex; unsigned int sample_rate; int ratio; @@ -357,12 +359,16 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) { + mutex_lock(&hdmi->audio_mutex); hdmi_set_clk_regenerator(hdmi, 74250000); + mutex_unlock(&hdmi->audio_mutex); } static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) { + mutex_lock(&hdmi->audio_mutex); hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); + mutex_unlock(&hdmi->audio_mutex); } /* @@ -1565,6 +1571,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master, hdmi->ratio = 100; hdmi->encoder = encoder; + mutex_init(&hdmi->audio_mutex); + of_property_read_u32(np, "reg-io-width", &val); switch (val) { -- cgit v1.2.3 From 622494a3560b13e05c9f2049bfab8e7fb12c67a2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 2 Feb 2015 10:55:38 +0000 Subject: drm: bridge/dw_hdmi: adjust n/cts setting order Patch derived from one from Yakir Yang. Yakir Yang says: For Designerware HDMI, the following write sequence is recommended: 1. aud_n3 (set bit ncts_atomic_write if desired) 2. aud_cts3 (set CTS_manual and CTS value if desired/enabled) 3. aud_cts2 (required in CTS_manual) 4. aud_cts1 (required in CTS_manual) 5. aud_n3 (bit ncts_atomic_write with same value as in step 1.) 6. aud_n2 7. aud_n1 However, avoid the ncts_atomic_write_bit and CTS_manual settings in this patch, both of which are marked reserved in the iMX6 documentation. All iMX6 code in the wild seems to want CTS_manual cleared. Having requested clarification from FSL, it appears that neither of these bits are implemented in their version of the IP. Acked-by: Philipp Zabel Tested-by: Philipp Zabel Acked-by: Andy Yan Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index b75922d4901e..cca1c3d165e2 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -182,20 +182,20 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, unsigned int n) { - hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); - hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); - hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3); + /* Must be set/cleared first */ + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); /* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); - /* Must be set/cleared first */ - hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); - - hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); - hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); + hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); + + hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3); + hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); } static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, -- cgit v1.2.3 From ca44b9d16423e5c8bd75328a217e5ed8d30c5b1d Mon Sep 17 00:00:00 2001 From: Yakir Yang Date: Tue, 31 Mar 2015 23:55:59 -0400 Subject: drm: bridge/dw_hdmi: fixed codec style Using a local struct pointer to reduce one level of indirection makes the code slightly more readable. Signed-off-by: Yakir Yang Reviewed-by: Daniel Kurtz Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index cca1c3d165e2..7943cedd63ce 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -755,10 +755,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, { unsigned res_idx, i; u8 val, msec; - const struct dw_hdmi_mpll_config *mpll_config = - hdmi->plat_data->mpll_cfg; - const struct dw_hdmi_curr_ctrl *curr_ctrl = hdmi->plat_data->cur_ctr; - const struct dw_hdmi_sym_term *sym_term = hdmi->plat_data->sym_term; + const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; + const struct dw_hdmi_mpll_config *mpll_config = plat_data->mpll_cfg; + const struct dw_hdmi_curr_ctrl *curr_ctrl = plat_data->cur_ctr; + const struct dw_hdmi_sym_term *sym_term = plat_data->sym_term; if (prep) return -EINVAL; -- cgit v1.2.3 From 034705a48bcae9d51661c461b7dd21e0d5b85afc Mon Sep 17 00:00:00 2001 From: Yakir Yang Date: Tue, 31 Mar 2015 23:56:10 -0400 Subject: drm: bridge/dw_hdmi: separate VLEVCTRL settting into platform driver Because of iMX6 & Rockchip have differnet mpll config parameter, the VLEVCTRL parameter would be different. In this case we should separate VLEVCTRL setting from the common dw_hdmi driver, config this parameter in platform driver(dw_hdmi-imx and dw_hdmi-rockchip) Signed-off-by: Yakir Yang Signed-off-by: Russell King --- drivers/gpu/drm/bridge/dw_hdmi.c | 14 +++++++------- drivers/gpu/drm/imx/dw_hdmi-imx.c | 24 ++++++++++++------------ drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 14 +++++++------- include/drm/bridge/dw_hdmi.h | 5 +++-- 4 files changed, 29 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 7943cedd63ce..49cafb61d290 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -758,7 +758,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = plat_data->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = plat_data->cur_ctr; - const struct dw_hdmi_sym_term *sym_term = plat_data->sym_term; + const struct dw_hdmi_phy_config *phy_config = plat_data->phy_config; if (prep) return -EINVAL; @@ -829,18 +829,18 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); - for (i = 0; sym_term[i].mpixelclock != (~0UL); i++) + for (i = 0; phy_config[i].mpixelclock != (~0UL); i++) if (hdmi->hdmi_data.video_mode.mpixelclock <= - sym_term[i].mpixelclock) + phy_config[i].mpixelclock) break; /* RESISTANCE TERM 133Ohm Cfg */ - hdmi_phy_i2c_write(hdmi, sym_term[i].term, 0x19); /* TXTERM */ + hdmi_phy_i2c_write(hdmi, phy_config[i].term, 0x19); /* TXTERM */ /* PREEMP Cgf 0.00 */ - hdmi_phy_i2c_write(hdmi, sym_term[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ - + hdmi_phy_i2c_write(hdmi, phy_config[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ /* TX/CK LVL 10 */ - hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */ + hdmi_phy_i2c_write(hdmi, phy_config[i].vlev_ctr, 0x0E); /* VLEVCTRL */ + /* REMOVE CLK TERM */ hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 121d30ca2d44..1032c07773e3 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -73,10 +73,10 @@ static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = { } }; -static const struct dw_hdmi_sym_term imx_sym_term[] = { - /*pixelclk symbol term*/ - { 148500000, 0x800d, 0x0005 }, - { ~0UL, 0x0000, 0x0000 } +static const struct dw_hdmi_phy_config imx_phy_config[] = { + /*pixelclk symbol term vlev */ + { 148500000, 0x800d, 0x0005, 0x01ad}, + { ~0UL, 0x0000, 0x0000, 0x0000} }; static int dw_hdmi_imx_parse_dt(struct imx_hdmi *hdmi) @@ -137,17 +137,17 @@ static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { }; static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { - .mpll_cfg = imx_mpll_cfg, - .cur_ctr = imx_cur_ctr, - .sym_term = imx_sym_term, - .dev_type = IMX6Q_HDMI, + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .phy_config = imx_phy_config, + .dev_type = IMX6Q_HDMI, }; static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { - .mpll_cfg = imx_mpll_cfg, - .cur_ctr = imx_cur_ctr, - .sym_term = imx_sym_term, - .dev_type = IMX6DL_HDMI, + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .phy_config = imx_phy_config, + .dev_type = IMX6DL_HDMI, }; static const struct of_device_id dw_hdmi_imx_dt_ids[] = { diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d236faa05b19..65b0f7bf5b52 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -133,12 +133,12 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { } }; -static const struct dw_hdmi_sym_term rockchip_sym_term[] = { - /*pixelclk symbol term*/ - { 74250000, 0x8009, 0x0004 }, - { 148500000, 0x8029, 0x0004 }, - { 297000000, 0x8039, 0x0005 }, - { ~0UL, 0x0000, 0x0000 } +static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x01ad}, + { 148500000, 0x8029, 0x0004, 0x01ad}, + { 297000000, 0x8039, 0x0005, 0x01ad}, + { ~0UL, 0x0000, 0x0000, 0x0000} }; static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) @@ -230,7 +230,7 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { .mode_valid = dw_hdmi_rockchip_mode_valid, .mpll_cfg = rockchip_mpll_cfg, .cur_ctr = rockchip_cur_ctr, - .sym_term = rockchip_sym_term, + .phy_config = rockchip_phy_config, .dev_type = RK3288_HDMI, }; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 5a4f49005169..de13bfc35634 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -38,17 +38,18 @@ struct dw_hdmi_curr_ctrl { u16 curr[DW_HDMI_RES_MAX]; }; -struct dw_hdmi_sym_term { +struct dw_hdmi_phy_config { unsigned long mpixelclock; u16 sym_ctr; /*clock symbol and transmitter control*/ u16 term; /*transmission termination value*/ + u16 vlev_ctr; /* voltage level control */ }; struct dw_hdmi_plat_data { enum dw_hdmi_devtype dev_type; const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; - const struct dw_hdmi_sym_term *sym_term; + const struct dw_hdmi_phy_config *phy_config; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); }; -- cgit v1.2.3