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