1 /* 2 * Copyright (C) 2013 Red Hat 3 * Author: Rob Clark <robdclark@gmail.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "hdmi.h" 19 20 struct hdmi_bridge { 21 struct drm_bridge base; 22 struct hdmi *hdmi; 23 }; 24 #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base) 25 26 static void hdmi_bridge_destroy(struct drm_bridge *bridge) 27 { 28 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 29 hdmi_unreference(hdmi_bridge->hdmi); 30 drm_bridge_cleanup(bridge); 31 kfree(hdmi_bridge); 32 } 33 34 static void power_on(struct drm_bridge *bridge) 35 { 36 struct drm_device *dev = bridge->dev; 37 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 38 struct hdmi *hdmi = hdmi_bridge->hdmi; 39 const struct hdmi_platform_config *config = hdmi->config; 40 int i, ret; 41 42 for (i = 0; i < config->pwr_reg_cnt; i++) { 43 ret = regulator_enable(hdmi->pwr_regs[i]); 44 if (ret) { 45 dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n", 46 config->pwr_reg_names[i], ret); 47 } 48 } 49 50 if (config->pwr_clk_cnt > 0) { 51 DBG("pixclock: %lu", hdmi->pixclock); 52 ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock); 53 if (ret) { 54 dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n", 55 config->pwr_clk_names[0], ret); 56 } 57 } 58 59 for (i = 0; i < config->pwr_clk_cnt; i++) { 60 ret = clk_prepare_enable(hdmi->pwr_clks[i]); 61 if (ret) { 62 dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n", 63 config->pwr_clk_names[i], ret); 64 } 65 } 66 } 67 68 static void power_off(struct drm_bridge *bridge) 69 { 70 struct drm_device *dev = bridge->dev; 71 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 72 struct hdmi *hdmi = hdmi_bridge->hdmi; 73 const struct hdmi_platform_config *config = hdmi->config; 74 int i, ret; 75 76 /* TODO do we need to wait for final vblank somewhere before 77 * cutting the clocks? 78 */ 79 mdelay(16 + 4); 80 81 for (i = 0; i < config->pwr_clk_cnt; i++) 82 clk_disable_unprepare(hdmi->pwr_clks[i]); 83 84 for (i = 0; i < config->pwr_reg_cnt; i++) { 85 ret = regulator_disable(hdmi->pwr_regs[i]); 86 if (ret) { 87 dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n", 88 config->pwr_reg_names[i], ret); 89 } 90 } 91 } 92 93 static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) 94 { 95 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 96 struct hdmi *hdmi = hdmi_bridge->hdmi; 97 struct hdmi_phy *phy = hdmi->phy; 98 99 DBG("power up"); 100 101 if (!hdmi->power_on) { 102 power_on(bridge); 103 hdmi->power_on = true; 104 hdmi_audio_update(hdmi); 105 } 106 107 phy->funcs->powerup(phy, hdmi->pixclock); 108 hdmi_set_mode(hdmi, true); 109 } 110 111 static void hdmi_bridge_enable(struct drm_bridge *bridge) 112 { 113 } 114 115 static void hdmi_bridge_disable(struct drm_bridge *bridge) 116 { 117 } 118 119 static void hdmi_bridge_post_disable(struct drm_bridge *bridge) 120 { 121 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 122 struct hdmi *hdmi = hdmi_bridge->hdmi; 123 struct hdmi_phy *phy = hdmi->phy; 124 125 DBG("power down"); 126 hdmi_set_mode(hdmi, false); 127 phy->funcs->powerdown(phy); 128 129 if (hdmi->power_on) { 130 power_off(bridge); 131 hdmi->power_on = false; 132 hdmi_audio_update(hdmi); 133 } 134 } 135 136 static void hdmi_bridge_mode_set(struct drm_bridge *bridge, 137 struct drm_display_mode *mode, 138 struct drm_display_mode *adjusted_mode) 139 { 140 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 141 struct hdmi *hdmi = hdmi_bridge->hdmi; 142 int hstart, hend, vstart, vend; 143 uint32_t frame_ctrl; 144 145 mode = adjusted_mode; 146 147 hdmi->pixclock = mode->clock * 1000; 148 149 hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1; 150 151 hstart = mode->htotal - mode->hsync_start; 152 hend = mode->htotal - mode->hsync_start + mode->hdisplay; 153 154 vstart = mode->vtotal - mode->vsync_start - 1; 155 vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1; 156 157 DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d", 158 mode->htotal, mode->vtotal, hstart, hend, vstart, vend); 159 160 hdmi_write(hdmi, REG_HDMI_TOTAL, 161 HDMI_TOTAL_H_TOTAL(mode->htotal - 1) | 162 HDMI_TOTAL_V_TOTAL(mode->vtotal - 1)); 163 164 hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC, 165 HDMI_ACTIVE_HSYNC_START(hstart) | 166 HDMI_ACTIVE_HSYNC_END(hend)); 167 hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC, 168 HDMI_ACTIVE_VSYNC_START(vstart) | 169 HDMI_ACTIVE_VSYNC_END(vend)); 170 171 if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 172 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, 173 HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal)); 174 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, 175 HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) | 176 HDMI_VSYNC_ACTIVE_F2_END(vend + 1)); 177 } else { 178 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, 179 HDMI_VSYNC_TOTAL_F2_V_TOTAL(0)); 180 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, 181 HDMI_VSYNC_ACTIVE_F2_START(0) | 182 HDMI_VSYNC_ACTIVE_F2_END(0)); 183 } 184 185 frame_ctrl = 0; 186 if (mode->flags & DRM_MODE_FLAG_NHSYNC) 187 frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW; 188 if (mode->flags & DRM_MODE_FLAG_NVSYNC) 189 frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW; 190 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 191 frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN; 192 DBG("frame_ctrl=%08x", frame_ctrl); 193 hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); 194 195 hdmi_audio_update(hdmi); 196 } 197 198 static const struct drm_bridge_funcs hdmi_bridge_funcs = { 199 .pre_enable = hdmi_bridge_pre_enable, 200 .enable = hdmi_bridge_enable, 201 .disable = hdmi_bridge_disable, 202 .post_disable = hdmi_bridge_post_disable, 203 .mode_set = hdmi_bridge_mode_set, 204 .destroy = hdmi_bridge_destroy, 205 }; 206 207 208 /* initialize bridge */ 209 struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) 210 { 211 struct drm_bridge *bridge = NULL; 212 struct hdmi_bridge *hdmi_bridge; 213 int ret; 214 215 hdmi_bridge = kzalloc(sizeof(*hdmi_bridge), GFP_KERNEL); 216 if (!hdmi_bridge) { 217 ret = -ENOMEM; 218 goto fail; 219 } 220 221 hdmi_bridge->hdmi = hdmi_reference(hdmi); 222 223 bridge = &hdmi_bridge->base; 224 225 drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs); 226 227 return bridge; 228 229 fail: 230 if (bridge) 231 hdmi_bridge_destroy(bridge); 232 233 return ERR_PTR(ret); 234 } 235