diff options
Diffstat (limited to 'drivers/gpu/drm/msm/hdmi/hdmi_bridge.c')
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 83 | 
1 files changed, 75 insertions, 8 deletions
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 5a8ee3473cf..f6cf745c249 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -19,10 +19,7 @@  struct hdmi_bridge {  	struct drm_bridge base; -  	struct hdmi *hdmi; - -	unsigned long int pixclock;  };  #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base) @@ -34,6 +31,65 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)  	kfree(hdmi_bridge);  } +static void power_on(struct drm_bridge *bridge) +{ +	struct drm_device *dev = bridge->dev; +	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); +	struct hdmi *hdmi = hdmi_bridge->hdmi; +	const struct hdmi_platform_config *config = hdmi->config; +	int i, ret; + +	for (i = 0; i < config->pwr_reg_cnt; i++) { +		ret = regulator_enable(hdmi->pwr_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n", +					config->pwr_reg_names[i], ret); +		} +	} + +	if (config->pwr_clk_cnt > 0) { +		DBG("pixclock: %lu", hdmi->pixclock); +		ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock); +		if (ret) { +			dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n", +					config->pwr_clk_names[0], ret); +		} +	} + +	for (i = 0; i < config->pwr_clk_cnt; i++) { +		ret = clk_prepare_enable(hdmi->pwr_clks[i]); +		if (ret) { +			dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n", +					config->pwr_clk_names[i], ret); +		} +	} +} + +static void power_off(struct drm_bridge *bridge) +{ +	struct drm_device *dev = bridge->dev; +	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); +	struct hdmi *hdmi = hdmi_bridge->hdmi; +	const struct hdmi_platform_config *config = hdmi->config; +	int i, ret; + +	/* TODO do we need to wait for final vblank somewhere before +	 * cutting the clocks? +	 */ +	mdelay(16 + 4); + +	for (i = 0; i < config->pwr_clk_cnt; i++) +		clk_disable_unprepare(hdmi->pwr_clks[i]); + +	for (i = 0; i < config->pwr_reg_cnt; i++) { +		ret = regulator_disable(hdmi->pwr_regs[i]); +		if (ret) { +			dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n", +					config->pwr_reg_names[i], ret); +		} +	} +} +  static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)  {  	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); @@ -41,7 +97,14 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)  	struct hdmi_phy *phy = hdmi->phy;  	DBG("power up"); -	phy->funcs->powerup(phy, hdmi_bridge->pixclock); + +	if (!hdmi->power_on) { +		power_on(bridge); +		hdmi->power_on = true; +		hdmi_audio_update(hdmi); +	} + +	phy->funcs->powerup(phy, hdmi->pixclock);  	hdmi_set_mode(hdmi, true);  } @@ -62,6 +125,12 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)  	DBG("power down");  	hdmi_set_mode(hdmi, false);  	phy->funcs->powerdown(phy); + +	if (hdmi->power_on) { +		power_off(bridge); +		hdmi->power_on = false; +		hdmi_audio_update(hdmi); +	}  }  static void hdmi_bridge_mode_set(struct drm_bridge *bridge, @@ -75,7 +144,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,  	mode = adjusted_mode; -	hdmi_bridge->pixclock = mode->clock * 1000; +	hdmi->pixclock = mode->clock * 1000;  	hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1; @@ -123,9 +192,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,  	DBG("frame_ctrl=%08x", frame_ctrl);  	hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); -	// TODO until we have audio, this might be safest: -	if (hdmi->hdmi_mode) -		hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); +	hdmi_audio_update(hdmi);  }  static const struct drm_bridge_funcs hdmi_bridge_funcs = {  | 
