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 23 struct hdmi *hdmi; 24 25 unsigned long int pixclock; 26 }; 27 #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base) 28 29 static void hdmi_bridge_destroy(struct drm_bridge *bridge) 30 { 31 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 32 hdmi_unreference(hdmi_bridge->hdmi); 33 drm_bridge_cleanup(bridge); 34 kfree(hdmi_bridge); 35 } 36 37 static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) 38 { 39 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 40 struct hdmi *hdmi = hdmi_bridge->hdmi; 41 struct hdmi_phy *phy = hdmi->phy; 42 43 DBG("power up"); 44 phy->funcs->powerup(phy, hdmi_bridge->pixclock); 45 hdmi_set_mode(hdmi, true); 46 } 47 48 static void hdmi_bridge_enable(struct drm_bridge *bridge) 49 { 50 } 51 52 static void hdmi_bridge_disable(struct drm_bridge *bridge) 53 { 54 } 55 56 static void hdmi_bridge_post_disable(struct drm_bridge *bridge) 57 { 58 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 59 struct hdmi *hdmi = hdmi_bridge->hdmi; 60 struct hdmi_phy *phy = hdmi->phy; 61 62 DBG("power down"); 63 hdmi_set_mode(hdmi, false); 64 phy->funcs->powerdown(phy); 65 } 66 67 static void hdmi_bridge_mode_set(struct drm_bridge *bridge, 68 struct drm_display_mode *mode, 69 struct drm_display_mode *adjusted_mode) 70 { 71 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); 72 struct hdmi *hdmi = hdmi_bridge->hdmi; 73 int hstart, hend, vstart, vend; 74 uint32_t frame_ctrl; 75 76 mode = adjusted_mode; 77 78 hdmi_bridge->pixclock = mode->clock * 1000; 79 80 hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1; 81 82 hstart = mode->htotal - mode->hsync_start; 83 hend = mode->htotal - mode->hsync_start + mode->hdisplay; 84 85 vstart = mode->vtotal - mode->vsync_start - 1; 86 vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1; 87 88 DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d", 89 mode->htotal, mode->vtotal, hstart, hend, vstart, vend); 90 91 hdmi_write(hdmi, REG_HDMI_TOTAL, 92 HDMI_TOTAL_H_TOTAL(mode->htotal - 1) | 93 HDMI_TOTAL_V_TOTAL(mode->vtotal - 1)); 94 95 hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC, 96 HDMI_ACTIVE_HSYNC_START(hstart) | 97 HDMI_ACTIVE_HSYNC_END(hend)); 98 hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC, 99 HDMI_ACTIVE_VSYNC_START(vstart) | 100 HDMI_ACTIVE_VSYNC_END(vend)); 101 102 if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 103 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, 104 HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal)); 105 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, 106 HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) | 107 HDMI_VSYNC_ACTIVE_F2_END(vend + 1)); 108 } else { 109 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, 110 HDMI_VSYNC_TOTAL_F2_V_TOTAL(0)); 111 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, 112 HDMI_VSYNC_ACTIVE_F2_START(0) | 113 HDMI_VSYNC_ACTIVE_F2_END(0)); 114 } 115 116 frame_ctrl = 0; 117 if (mode->flags & DRM_MODE_FLAG_NHSYNC) 118 frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW; 119 if (mode->flags & DRM_MODE_FLAG_NVSYNC) 120 frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW; 121 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 122 frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN; 123 DBG("frame_ctrl=%08x", frame_ctrl); 124 hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); 125 126 // TODO until we have audio, this might be safest: 127 if (hdmi->hdmi_mode) 128 hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); 129 } 130 131 static const struct drm_bridge_funcs hdmi_bridge_funcs = { 132 .pre_enable = hdmi_bridge_pre_enable, 133 .enable = hdmi_bridge_enable, 134 .disable = hdmi_bridge_disable, 135 .post_disable = hdmi_bridge_post_disable, 136 .mode_set = hdmi_bridge_mode_set, 137 .destroy = hdmi_bridge_destroy, 138 }; 139 140 141 /* initialize bridge */ 142 struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) 143 { 144 struct drm_bridge *bridge = NULL; 145 struct hdmi_bridge *hdmi_bridge; 146 int ret; 147 148 hdmi_bridge = kzalloc(sizeof(*hdmi_bridge), GFP_KERNEL); 149 if (!hdmi_bridge) { 150 ret = -ENOMEM; 151 goto fail; 152 } 153 154 hdmi_bridge->hdmi = hdmi_reference(hdmi); 155 156 bridge = &hdmi_bridge->base; 157 158 drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs); 159 160 return bridge; 161 162 fail: 163 if (bridge) 164 hdmi_bridge_destroy(bridge); 165 166 return ERR_PTR(ret); 167 } 168