Commit 19a82b85245289d95cde2fb7715fe562ae700f59
1 parent
e533720df3
Exists in
smarc-ti-linux-3.14.y
and in
1 other branch
OMAPDSS: HDMI: Do not abort audio playback when display is turned off
Do not abort audio playback when display is turned off. The audio DMA stops when the display turned off and the audio stream will timeout in couple of seconds unless the display is enabled again in time. Without this patch the audio playback is aborted immediately when display is turned off. Signed-off-by: Jyri Sarha <jsarha@ti.com>
Showing 2 changed files with 4 additions and 4 deletions Inline Diff
drivers/video/fbdev/omap2/dss/hdmi4.c
1 | /* | 1 | /* |
2 | * HDMI interface DSS driver for TI's OMAP4 family of SoCs. | 2 | * HDMI interface DSS driver for TI's OMAP4 family of SoCs. |
3 | * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ | 3 | * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ |
4 | * Authors: Yong Zhi | 4 | * Authors: Yong Zhi |
5 | * Mythri pk <mythripk@ti.com> | 5 | * Mythri pk <mythripk@ti.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of the GNU General Public License version 2 as published by | 8 | * under the terms of the GNU General Public License version 2 as published by |
9 | * the Free Software Foundation. | 9 | * the Free Software Foundation. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | 11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. | 14 | * more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License along with | 16 | * You should have received a copy of the GNU General Public License along with |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #define DSS_SUBSYS_NAME "HDMI" | 20 | #define DSS_SUBSYS_NAME "HDMI" |
21 | 21 | ||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/mutex.h> | 27 | #include <linux/mutex.h> |
28 | #include <linux/delay.h> | 28 | #include <linux/delay.h> |
29 | #include <linux/string.h> | 29 | #include <linux/string.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/pm_runtime.h> | 31 | #include <linux/pm_runtime.h> |
32 | #include <linux/clk.h> | 32 | #include <linux/clk.h> |
33 | #include <linux/gpio.h> | 33 | #include <linux/gpio.h> |
34 | #include <linux/regulator/consumer.h> | 34 | #include <linux/regulator/consumer.h> |
35 | #include <video/omapdss.h> | 35 | #include <video/omapdss.h> |
36 | #include <sound/omap-hdmi-audio.h> | 36 | #include <sound/omap-hdmi-audio.h> |
37 | 37 | ||
38 | #include "hdmi4_core.h" | 38 | #include "hdmi4_core.h" |
39 | #include "dss.h" | 39 | #include "dss.h" |
40 | #include "dss_features.h" | 40 | #include "dss_features.h" |
41 | #include "hdmi.h" | 41 | #include "hdmi.h" |
42 | 42 | ||
43 | static struct omap_hdmi hdmi; | 43 | static struct omap_hdmi hdmi; |
44 | 44 | ||
45 | static int hdmi_runtime_get(void) | 45 | static int hdmi_runtime_get(void) |
46 | { | 46 | { |
47 | int r; | 47 | int r; |
48 | 48 | ||
49 | DSSDBG("hdmi_runtime_get\n"); | 49 | DSSDBG("hdmi_runtime_get\n"); |
50 | 50 | ||
51 | r = pm_runtime_get_sync(&hdmi.pdev->dev); | 51 | r = pm_runtime_get_sync(&hdmi.pdev->dev); |
52 | WARN_ON(r < 0); | 52 | WARN_ON(r < 0); |
53 | if (r < 0) | 53 | if (r < 0) |
54 | return r; | 54 | return r; |
55 | 55 | ||
56 | return 0; | 56 | return 0; |
57 | } | 57 | } |
58 | 58 | ||
59 | static void hdmi_runtime_put(void) | 59 | static void hdmi_runtime_put(void) |
60 | { | 60 | { |
61 | int r; | 61 | int r; |
62 | 62 | ||
63 | DSSDBG("hdmi_runtime_put\n"); | 63 | DSSDBG("hdmi_runtime_put\n"); |
64 | 64 | ||
65 | r = pm_runtime_put_sync(&hdmi.pdev->dev); | 65 | r = pm_runtime_put_sync(&hdmi.pdev->dev); |
66 | WARN_ON(r < 0 && r != -ENOSYS); | 66 | WARN_ON(r < 0 && r != -ENOSYS); |
67 | } | 67 | } |
68 | 68 | ||
69 | static irqreturn_t hdmi_irq_handler(int irq, void *data) | 69 | static irqreturn_t hdmi_irq_handler(int irq, void *data) |
70 | { | 70 | { |
71 | struct hdmi_wp_data *wp = data; | 71 | struct hdmi_wp_data *wp = data; |
72 | u32 irqstatus; | 72 | u32 irqstatus; |
73 | 73 | ||
74 | irqstatus = hdmi_wp_get_irqstatus(wp); | 74 | irqstatus = hdmi_wp_get_irqstatus(wp); |
75 | hdmi_wp_set_irqstatus(wp, irqstatus); | 75 | hdmi_wp_set_irqstatus(wp, irqstatus); |
76 | 76 | ||
77 | if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && | 77 | if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && |
78 | irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | 78 | irqstatus & HDMI_IRQ_LINK_DISCONNECT) { |
79 | /* | 79 | /* |
80 | * If we get both connect and disconnect interrupts at the same | 80 | * If we get both connect and disconnect interrupts at the same |
81 | * time, turn off the PHY, clear interrupts, and restart, which | 81 | * time, turn off the PHY, clear interrupts, and restart, which |
82 | * raises connect interrupt if a cable is connected, or nothing | 82 | * raises connect interrupt if a cable is connected, or nothing |
83 | * if cable is not connected. | 83 | * if cable is not connected. |
84 | */ | 84 | */ |
85 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); | 85 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); |
86 | 86 | ||
87 | hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | | 87 | hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | |
88 | HDMI_IRQ_LINK_DISCONNECT); | 88 | HDMI_IRQ_LINK_DISCONNECT); |
89 | 89 | ||
90 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | 90 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
91 | } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { | 91 | } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { |
92 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); | 92 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); |
93 | } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | 93 | } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { |
94 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | 94 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
95 | } | 95 | } |
96 | 96 | ||
97 | return IRQ_HANDLED; | 97 | return IRQ_HANDLED; |
98 | } | 98 | } |
99 | 99 | ||
100 | static int hdmi_init_regulator(void) | 100 | static int hdmi_init_regulator(void) |
101 | { | 101 | { |
102 | int r; | 102 | int r; |
103 | struct regulator *reg; | 103 | struct regulator *reg; |
104 | 104 | ||
105 | if (hdmi.vdda_reg != NULL) | 105 | if (hdmi.vdda_reg != NULL) |
106 | return 0; | 106 | return 0; |
107 | 107 | ||
108 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); | 108 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); |
109 | 109 | ||
110 | if (IS_ERR(reg)) { | 110 | if (IS_ERR(reg)) { |
111 | if (PTR_ERR(reg) != -EPROBE_DEFER) | 111 | if (PTR_ERR(reg) != -EPROBE_DEFER) |
112 | DSSERR("can't get VDDA regulator\n"); | 112 | DSSERR("can't get VDDA regulator\n"); |
113 | return PTR_ERR(reg); | 113 | return PTR_ERR(reg); |
114 | } | 114 | } |
115 | 115 | ||
116 | if (regulator_can_change_voltage(reg)) { | 116 | if (regulator_can_change_voltage(reg)) { |
117 | r = regulator_set_voltage(reg, 1800000, 1800000); | 117 | r = regulator_set_voltage(reg, 1800000, 1800000); |
118 | if (r) { | 118 | if (r) { |
119 | devm_regulator_put(reg); | 119 | devm_regulator_put(reg); |
120 | DSSWARN("can't set the regulator voltage\n"); | 120 | DSSWARN("can't set the regulator voltage\n"); |
121 | return r; | 121 | return r; |
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | hdmi.vdda_reg = reg; | 125 | hdmi.vdda_reg = reg; |
126 | 126 | ||
127 | return 0; | 127 | return 0; |
128 | } | 128 | } |
129 | 129 | ||
130 | static int hdmi_pll_enable(struct pll_data *pll, struct hdmi_wp_data *wp, | 130 | static int hdmi_pll_enable(struct pll_data *pll, struct hdmi_wp_data *wp, |
131 | struct pll_params *p) | 131 | struct pll_params *p) |
132 | { | 132 | { |
133 | u16 r = 0; | 133 | u16 r = 0; |
134 | 134 | ||
135 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | 135 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
136 | if (r) | 136 | if (r) |
137 | return r; | 137 | return r; |
138 | 138 | ||
139 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); | 139 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); |
140 | if (r) | 140 | if (r) |
141 | return r; | 141 | return r; |
142 | 142 | ||
143 | pll_sysreset(pll); | 143 | pll_sysreset(pll); |
144 | 144 | ||
145 | r = pll_wait_reset(pll); | 145 | r = pll_wait_reset(pll); |
146 | if (r) | 146 | if (r) |
147 | return r; | 147 | return r; |
148 | 148 | ||
149 | r = pll_calc_and_check_clock_rates(pll, p); | 149 | r = pll_calc_and_check_clock_rates(pll, p); |
150 | if (r) | 150 | if (r) |
151 | return r; | 151 | return r; |
152 | 152 | ||
153 | r = pll_set_clock_div(pll, p); | 153 | r = pll_set_clock_div(pll, p); |
154 | if (r) | 154 | if (r) |
155 | return r; | 155 | return r; |
156 | 156 | ||
157 | return 0; | 157 | return 0; |
158 | } | 158 | } |
159 | 159 | ||
160 | static void hdmi_pll_disable(struct hdmi_wp_data *wp) | 160 | static void hdmi_pll_disable(struct hdmi_wp_data *wp) |
161 | { | 161 | { |
162 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | 162 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
163 | } | 163 | } |
164 | 164 | ||
165 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) | 165 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) |
166 | { | 166 | { |
167 | int r; | 167 | int r; |
168 | 168 | ||
169 | r = regulator_enable(hdmi.vdda_reg); | 169 | r = regulator_enable(hdmi.vdda_reg); |
170 | if (r) | 170 | if (r) |
171 | return r; | 171 | return r; |
172 | 172 | ||
173 | r = hdmi_runtime_get(); | 173 | r = hdmi_runtime_get(); |
174 | if (r) | 174 | if (r) |
175 | goto err_runtime_get; | 175 | goto err_runtime_get; |
176 | 176 | ||
177 | /* Make selection of HDMI in DSS */ | 177 | /* Make selection of HDMI in DSS */ |
178 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | 178 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); |
179 | 179 | ||
180 | hdmi.core_enabled = true; | 180 | hdmi.core_enabled = true; |
181 | 181 | ||
182 | return 0; | 182 | return 0; |
183 | 183 | ||
184 | err_runtime_get: | 184 | err_runtime_get: |
185 | regulator_disable(hdmi.vdda_reg); | 185 | regulator_disable(hdmi.vdda_reg); |
186 | 186 | ||
187 | return r; | 187 | return r; |
188 | } | 188 | } |
189 | 189 | ||
190 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) | 190 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) |
191 | { | 191 | { |
192 | hdmi.core_enabled = false; | 192 | hdmi.core_enabled = false; |
193 | 193 | ||
194 | hdmi_runtime_put(); | 194 | hdmi_runtime_put(); |
195 | regulator_disable(hdmi.vdda_reg); | 195 | regulator_disable(hdmi.vdda_reg); |
196 | } | 196 | } |
197 | 197 | ||
198 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) | 198 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) |
199 | { | 199 | { |
200 | int r; | 200 | int r; |
201 | struct omap_video_timings *p; | 201 | struct omap_video_timings *p; |
202 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 202 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
203 | unsigned long phy; | 203 | unsigned long phy; |
204 | struct hdmi_wp_data *wp = &hdmi.wp; | 204 | struct hdmi_wp_data *wp = &hdmi.wp; |
205 | struct pll_params param; | 205 | struct pll_params param; |
206 | bool ok; | 206 | bool ok; |
207 | 207 | ||
208 | r = hdmi_power_on_core(dssdev); | 208 | r = hdmi_power_on_core(dssdev); |
209 | if (r) | 209 | if (r) |
210 | return r; | 210 | return r; |
211 | 211 | ||
212 | /* disable and clear irqs */ | 212 | /* disable and clear irqs */ |
213 | hdmi_wp_clear_irqenable(wp, 0xffffffff); | 213 | hdmi_wp_clear_irqenable(wp, 0xffffffff); |
214 | hdmi_wp_set_irqstatus(wp, 0xffffffff); | 214 | hdmi_wp_set_irqstatus(wp, 0xffffffff); |
215 | 215 | ||
216 | p = &hdmi.cfg.timings; | 216 | p = &hdmi.cfg.timings; |
217 | 217 | ||
218 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | 218 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
219 | 219 | ||
220 | phy = p->pixelclock * 10; | 220 | phy = p->pixelclock * 10; |
221 | 221 | ||
222 | memset(¶m, 0, sizeof(param)); | 222 | memset(¶m, 0, sizeof(param)); |
223 | 223 | ||
224 | ok = pll_calc(hdmi.pll, phy, phy, NULL, ¶m); | 224 | ok = pll_calc(hdmi.pll, phy, phy, NULL, ¶m); |
225 | if (!ok) { | 225 | if (!ok) { |
226 | DSSDBG("Failed to calculate PLL settings\n"); | 226 | DSSDBG("Failed to calculate PLL settings\n"); |
227 | goto err_pll_enable; | 227 | goto err_pll_enable; |
228 | } | 228 | } |
229 | 229 | ||
230 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ | 230 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ |
231 | r = hdmi_pll_enable(hdmi.pll, &hdmi.wp, ¶m); | 231 | r = hdmi_pll_enable(hdmi.pll, &hdmi.wp, ¶m); |
232 | if (r) { | 232 | if (r) { |
233 | DSSDBG("Failed to lock PLL\n"); | 233 | DSSDBG("Failed to lock PLL\n"); |
234 | goto err_pll_enable; | 234 | goto err_pll_enable; |
235 | } | 235 | } |
236 | 236 | ||
237 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | 237 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); |
238 | if (r) { | 238 | if (r) { |
239 | DSSDBG("Failed to configure PHY\n"); | 239 | DSSDBG("Failed to configure PHY\n"); |
240 | goto err_phy_cfg; | 240 | goto err_phy_cfg; |
241 | } | 241 | } |
242 | 242 | ||
243 | r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | 243 | r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
244 | if (r) | 244 | if (r) |
245 | goto err_phy_pwr; | 245 | goto err_phy_pwr; |
246 | 246 | ||
247 | hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); | 247 | hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); |
248 | 248 | ||
249 | /* bypass TV gamma table */ | 249 | /* bypass TV gamma table */ |
250 | dispc_enable_gamma_table(0); | 250 | dispc_enable_gamma_table(0); |
251 | 251 | ||
252 | /* tv size */ | 252 | /* tv size */ |
253 | dss_mgr_set_timings(mgr, p); | 253 | dss_mgr_set_timings(mgr, p); |
254 | 254 | ||
255 | r = dss_mgr_enable(mgr); | 255 | r = dss_mgr_enable(mgr); |
256 | if (r) | 256 | if (r) |
257 | goto err_mgr_enable; | 257 | goto err_mgr_enable; |
258 | 258 | ||
259 | r = hdmi_wp_video_start(&hdmi.wp); | 259 | r = hdmi_wp_video_start(&hdmi.wp); |
260 | if (r) | 260 | if (r) |
261 | goto err_vid_enable; | 261 | goto err_vid_enable; |
262 | 262 | ||
263 | hdmi_wp_set_irqenable(wp, | 263 | hdmi_wp_set_irqenable(wp, |
264 | HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); | 264 | HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); |
265 | 265 | ||
266 | return 0; | 266 | return 0; |
267 | 267 | ||
268 | err_vid_enable: | 268 | err_vid_enable: |
269 | dss_mgr_disable(mgr); | 269 | dss_mgr_disable(mgr); |
270 | err_mgr_enable: | 270 | err_mgr_enable: |
271 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | 271 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); |
272 | err_phy_pwr: | 272 | err_phy_pwr: |
273 | err_phy_cfg: | 273 | err_phy_cfg: |
274 | hdmi_pll_disable(&hdmi.wp); | 274 | hdmi_pll_disable(&hdmi.wp); |
275 | err_pll_enable: | 275 | err_pll_enable: |
276 | hdmi_power_off_core(dssdev); | 276 | hdmi_power_off_core(dssdev); |
277 | return -EIO; | 277 | return -EIO; |
278 | } | 278 | } |
279 | 279 | ||
280 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) | 280 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) |
281 | { | 281 | { |
282 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 282 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
283 | 283 | ||
284 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | 284 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); |
285 | 285 | ||
286 | hdmi_wp_video_stop(&hdmi.wp); | 286 | hdmi_wp_video_stop(&hdmi.wp); |
287 | 287 | ||
288 | dss_mgr_disable(mgr); | 288 | dss_mgr_disable(mgr); |
289 | 289 | ||
290 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | 290 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); |
291 | 291 | ||
292 | hdmi_pll_disable(&hdmi.wp); | 292 | hdmi_pll_disable(&hdmi.wp); |
293 | 293 | ||
294 | hdmi_power_off_core(dssdev); | 294 | hdmi_power_off_core(dssdev); |
295 | } | 295 | } |
296 | 296 | ||
297 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, | 297 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, |
298 | struct omap_video_timings *timings) | 298 | struct omap_video_timings *timings) |
299 | { | 299 | { |
300 | struct omap_dss_device *out = &hdmi.output; | 300 | struct omap_dss_device *out = &hdmi.output; |
301 | 301 | ||
302 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) | 302 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) |
303 | return -EINVAL; | 303 | return -EINVAL; |
304 | 304 | ||
305 | return 0; | 305 | return 0; |
306 | } | 306 | } |
307 | 307 | ||
308 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, | 308 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, |
309 | struct omap_video_timings *timings) | 309 | struct omap_video_timings *timings) |
310 | { | 310 | { |
311 | mutex_lock(&hdmi.lock); | 311 | mutex_lock(&hdmi.lock); |
312 | 312 | ||
313 | hdmi.cfg.timings = *timings; | 313 | hdmi.cfg.timings = *timings; |
314 | 314 | ||
315 | dispc_set_tv_pclk(timings->pixelclock); | 315 | dispc_set_tv_pclk(timings->pixelclock); |
316 | 316 | ||
317 | mutex_unlock(&hdmi.lock); | 317 | mutex_unlock(&hdmi.lock); |
318 | } | 318 | } |
319 | 319 | ||
320 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, | 320 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, |
321 | struct omap_video_timings *timings) | 321 | struct omap_video_timings *timings) |
322 | { | 322 | { |
323 | *timings = hdmi.cfg.timings; | 323 | *timings = hdmi.cfg.timings; |
324 | } | 324 | } |
325 | 325 | ||
326 | static void hdmi_dump_regs(struct seq_file *s) | 326 | static void hdmi_dump_regs(struct seq_file *s) |
327 | { | 327 | { |
328 | mutex_lock(&hdmi.lock); | 328 | mutex_lock(&hdmi.lock); |
329 | 329 | ||
330 | if (hdmi_runtime_get()) { | 330 | if (hdmi_runtime_get()) { |
331 | mutex_unlock(&hdmi.lock); | 331 | mutex_unlock(&hdmi.lock); |
332 | return; | 332 | return; |
333 | } | 333 | } |
334 | 334 | ||
335 | hdmi_wp_dump(&hdmi.wp, s); | 335 | hdmi_wp_dump(&hdmi.wp, s); |
336 | pll_dump(hdmi.pll, s); | 336 | pll_dump(hdmi.pll, s); |
337 | hdmi_phy_dump(&hdmi.phy, s); | 337 | hdmi_phy_dump(&hdmi.phy, s); |
338 | hdmi4_core_dump(&hdmi.core, s); | 338 | hdmi4_core_dump(&hdmi.core, s); |
339 | 339 | ||
340 | hdmi_runtime_put(); | 340 | hdmi_runtime_put(); |
341 | mutex_unlock(&hdmi.lock); | 341 | mutex_unlock(&hdmi.lock); |
342 | } | 342 | } |
343 | 343 | ||
344 | static int read_edid(u8 *buf, int len) | 344 | static int read_edid(u8 *buf, int len) |
345 | { | 345 | { |
346 | int r; | 346 | int r; |
347 | 347 | ||
348 | mutex_lock(&hdmi.lock); | 348 | mutex_lock(&hdmi.lock); |
349 | 349 | ||
350 | r = hdmi_runtime_get(); | 350 | r = hdmi_runtime_get(); |
351 | BUG_ON(r); | 351 | BUG_ON(r); |
352 | 352 | ||
353 | r = hdmi4_read_edid(&hdmi.core, buf, len); | 353 | r = hdmi4_read_edid(&hdmi.core, buf, len); |
354 | 354 | ||
355 | hdmi_runtime_put(); | 355 | hdmi_runtime_put(); |
356 | mutex_unlock(&hdmi.lock); | 356 | mutex_unlock(&hdmi.lock); |
357 | 357 | ||
358 | return r; | 358 | return r; |
359 | } | 359 | } |
360 | 360 | ||
361 | static int hdmi_display_enable(struct omap_dss_device *dssdev) | 361 | static int hdmi_display_enable(struct omap_dss_device *dssdev) |
362 | { | 362 | { |
363 | struct omap_dss_device *out = &hdmi.output; | 363 | struct omap_dss_device *out = &hdmi.output; |
364 | int r = 0; | 364 | int r = 0; |
365 | 365 | ||
366 | DSSDBG("ENTER hdmi_display_enable\n"); | 366 | DSSDBG("ENTER hdmi_display_enable\n"); |
367 | 367 | ||
368 | mutex_lock(&hdmi.lock); | 368 | mutex_lock(&hdmi.lock); |
369 | 369 | ||
370 | if (out == NULL || out->manager == NULL) { | 370 | if (out == NULL || out->manager == NULL) { |
371 | DSSERR("failed to enable display: no output/manager\n"); | 371 | DSSERR("failed to enable display: no output/manager\n"); |
372 | r = -ENODEV; | 372 | r = -ENODEV; |
373 | goto err0; | 373 | goto err0; |
374 | } | 374 | } |
375 | 375 | ||
376 | r = hdmi_power_on_full(dssdev); | 376 | r = hdmi_power_on_full(dssdev); |
377 | if (r) { | 377 | if (r) { |
378 | DSSERR("failed to power on device\n"); | 378 | DSSERR("failed to power on device\n"); |
379 | goto err0; | 379 | goto err0; |
380 | } | 380 | } |
381 | 381 | ||
382 | hdmi.display_enabled = true; | 382 | hdmi.display_enabled = true; |
383 | 383 | ||
384 | mutex_unlock(&hdmi.lock); | 384 | mutex_unlock(&hdmi.lock); |
385 | return 0; | 385 | return 0; |
386 | 386 | ||
387 | err0: | 387 | err0: |
388 | mutex_unlock(&hdmi.lock); | 388 | mutex_unlock(&hdmi.lock); |
389 | return r; | 389 | return r; |
390 | } | 390 | } |
391 | 391 | ||
392 | static void hdmi_display_disable(struct omap_dss_device *dssdev) | 392 | static void hdmi_display_disable(struct omap_dss_device *dssdev) |
393 | { | 393 | { |
394 | DSSDBG("Enter hdmi_display_disable\n"); | 394 | DSSDBG("Enter hdmi_display_disable\n"); |
395 | 395 | ||
396 | mutex_lock(&hdmi.lock); | 396 | mutex_lock(&hdmi.lock); |
397 | 397 | ||
398 | if (hdmi.audio_abort_cb) | 398 | /* If set hdmi.audio_abort_cb(&hdmi.pdev->dev) should be |
399 | hdmi.audio_abort_cb(&hdmi.pdev->dev); | 399 | * called here, if audio abort functionality is needed. */ |
400 | 400 | ||
401 | hdmi_power_off_full(dssdev); | 401 | hdmi_power_off_full(dssdev); |
402 | 402 | ||
403 | hdmi.display_enabled = false; | 403 | hdmi.display_enabled = false; |
404 | 404 | ||
405 | mutex_unlock(&hdmi.lock); | 405 | mutex_unlock(&hdmi.lock); |
406 | } | 406 | } |
407 | 407 | ||
408 | static int hdmi_core_enable(struct omap_dss_device *dssdev) | 408 | static int hdmi_core_enable(struct omap_dss_device *dssdev) |
409 | { | 409 | { |
410 | int r = 0; | 410 | int r = 0; |
411 | 411 | ||
412 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | 412 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); |
413 | 413 | ||
414 | mutex_lock(&hdmi.lock); | 414 | mutex_lock(&hdmi.lock); |
415 | 415 | ||
416 | r = hdmi_power_on_core(dssdev); | 416 | r = hdmi_power_on_core(dssdev); |
417 | if (r) { | 417 | if (r) { |
418 | DSSERR("failed to power on device\n"); | 418 | DSSERR("failed to power on device\n"); |
419 | goto err0; | 419 | goto err0; |
420 | } | 420 | } |
421 | 421 | ||
422 | mutex_unlock(&hdmi.lock); | 422 | mutex_unlock(&hdmi.lock); |
423 | return 0; | 423 | return 0; |
424 | 424 | ||
425 | err0: | 425 | err0: |
426 | mutex_unlock(&hdmi.lock); | 426 | mutex_unlock(&hdmi.lock); |
427 | return r; | 427 | return r; |
428 | } | 428 | } |
429 | 429 | ||
430 | static void hdmi_core_disable(struct omap_dss_device *dssdev) | 430 | static void hdmi_core_disable(struct omap_dss_device *dssdev) |
431 | { | 431 | { |
432 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); | 432 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); |
433 | 433 | ||
434 | mutex_lock(&hdmi.lock); | 434 | mutex_lock(&hdmi.lock); |
435 | 435 | ||
436 | hdmi_power_off_core(dssdev); | 436 | hdmi_power_off_core(dssdev); |
437 | 437 | ||
438 | mutex_unlock(&hdmi.lock); | 438 | mutex_unlock(&hdmi.lock); |
439 | } | 439 | } |
440 | 440 | ||
441 | static int hdmi_connect(struct omap_dss_device *dssdev, | 441 | static int hdmi_connect(struct omap_dss_device *dssdev, |
442 | struct omap_dss_device *dst) | 442 | struct omap_dss_device *dst) |
443 | { | 443 | { |
444 | struct omap_overlay_manager *mgr; | 444 | struct omap_overlay_manager *mgr; |
445 | int r; | 445 | int r; |
446 | 446 | ||
447 | r = hdmi_init_regulator(); | 447 | r = hdmi_init_regulator(); |
448 | if (r) | 448 | if (r) |
449 | return r; | 449 | return r; |
450 | 450 | ||
451 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | 451 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); |
452 | if (!mgr) | 452 | if (!mgr) |
453 | return -ENODEV; | 453 | return -ENODEV; |
454 | 454 | ||
455 | r = dss_mgr_connect(mgr, dssdev); | 455 | r = dss_mgr_connect(mgr, dssdev); |
456 | if (r) | 456 | if (r) |
457 | return r; | 457 | return r; |
458 | 458 | ||
459 | r = omapdss_output_set_device(dssdev, dst); | 459 | r = omapdss_output_set_device(dssdev, dst); |
460 | if (r) { | 460 | if (r) { |
461 | DSSERR("failed to connect output to new device: %s\n", | 461 | DSSERR("failed to connect output to new device: %s\n", |
462 | dst->name); | 462 | dst->name); |
463 | dss_mgr_disconnect(mgr, dssdev); | 463 | dss_mgr_disconnect(mgr, dssdev); |
464 | return r; | 464 | return r; |
465 | } | 465 | } |
466 | 466 | ||
467 | return 0; | 467 | return 0; |
468 | } | 468 | } |
469 | 469 | ||
470 | static void hdmi_disconnect(struct omap_dss_device *dssdev, | 470 | static void hdmi_disconnect(struct omap_dss_device *dssdev, |
471 | struct omap_dss_device *dst) | 471 | struct omap_dss_device *dst) |
472 | { | 472 | { |
473 | WARN_ON(dst != dssdev->dst); | 473 | WARN_ON(dst != dssdev->dst); |
474 | 474 | ||
475 | if (dst != dssdev->dst) | 475 | if (dst != dssdev->dst) |
476 | return; | 476 | return; |
477 | 477 | ||
478 | omapdss_output_unset_device(dssdev); | 478 | omapdss_output_unset_device(dssdev); |
479 | 479 | ||
480 | if (dssdev->manager) | 480 | if (dssdev->manager) |
481 | dss_mgr_disconnect(dssdev->manager, dssdev); | 481 | dss_mgr_disconnect(dssdev->manager, dssdev); |
482 | } | 482 | } |
483 | 483 | ||
484 | static int hdmi_read_edid(struct omap_dss_device *dssdev, | 484 | static int hdmi_read_edid(struct omap_dss_device *dssdev, |
485 | u8 *edid, int len) | 485 | u8 *edid, int len) |
486 | { | 486 | { |
487 | bool need_enable; | 487 | bool need_enable; |
488 | int r; | 488 | int r; |
489 | 489 | ||
490 | need_enable = hdmi.core_enabled == false; | 490 | need_enable = hdmi.core_enabled == false; |
491 | 491 | ||
492 | if (need_enable) { | 492 | if (need_enable) { |
493 | r = hdmi_core_enable(dssdev); | 493 | r = hdmi_core_enable(dssdev); |
494 | if (r) | 494 | if (r) |
495 | return r; | 495 | return r; |
496 | } | 496 | } |
497 | 497 | ||
498 | r = read_edid(edid, len); | 498 | r = read_edid(edid, len); |
499 | 499 | ||
500 | if (need_enable) | 500 | if (need_enable) |
501 | hdmi_core_disable(dssdev); | 501 | hdmi_core_disable(dssdev); |
502 | 502 | ||
503 | return r; | 503 | return r; |
504 | } | 504 | } |
505 | 505 | ||
506 | static int hdmi_set_infoframe(struct omap_dss_device *dssdev, | 506 | static int hdmi_set_infoframe(struct omap_dss_device *dssdev, |
507 | const struct hdmi_avi_infoframe *avi) | 507 | const struct hdmi_avi_infoframe *avi) |
508 | { | 508 | { |
509 | hdmi.cfg.infoframe = *avi; | 509 | hdmi.cfg.infoframe = *avi; |
510 | return 0; | 510 | return 0; |
511 | } | 511 | } |
512 | 512 | ||
513 | static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, | 513 | static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, |
514 | bool hdmi_mode) | 514 | bool hdmi_mode) |
515 | { | 515 | { |
516 | hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; | 516 | hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; |
517 | return 0; | 517 | return 0; |
518 | } | 518 | } |
519 | 519 | ||
520 | static const struct omapdss_hdmi_ops hdmi_ops = { | 520 | static const struct omapdss_hdmi_ops hdmi_ops = { |
521 | .connect = hdmi_connect, | 521 | .connect = hdmi_connect, |
522 | .disconnect = hdmi_disconnect, | 522 | .disconnect = hdmi_disconnect, |
523 | 523 | ||
524 | .enable = hdmi_display_enable, | 524 | .enable = hdmi_display_enable, |
525 | .disable = hdmi_display_disable, | 525 | .disable = hdmi_display_disable, |
526 | 526 | ||
527 | .check_timings = hdmi_display_check_timing, | 527 | .check_timings = hdmi_display_check_timing, |
528 | .set_timings = hdmi_display_set_timing, | 528 | .set_timings = hdmi_display_set_timing, |
529 | .get_timings = hdmi_display_get_timings, | 529 | .get_timings = hdmi_display_get_timings, |
530 | 530 | ||
531 | .read_edid = hdmi_read_edid, | 531 | .read_edid = hdmi_read_edid, |
532 | .set_infoframe = hdmi_set_infoframe, | 532 | .set_infoframe = hdmi_set_infoframe, |
533 | .set_hdmi_mode = hdmi_set_hdmi_mode, | 533 | .set_hdmi_mode = hdmi_set_hdmi_mode, |
534 | }; | 534 | }; |
535 | 535 | ||
536 | static void hdmi_init_output(struct platform_device *pdev) | 536 | static void hdmi_init_output(struct platform_device *pdev) |
537 | { | 537 | { |
538 | struct omap_dss_device *out = &hdmi.output; | 538 | struct omap_dss_device *out = &hdmi.output; |
539 | 539 | ||
540 | out->dev = &pdev->dev; | 540 | out->dev = &pdev->dev; |
541 | out->id = OMAP_DSS_OUTPUT_HDMI; | 541 | out->id = OMAP_DSS_OUTPUT_HDMI; |
542 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; | 542 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; |
543 | out->name = "hdmi.0"; | 543 | out->name = "hdmi.0"; |
544 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | 544 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
545 | out->ops.hdmi = &hdmi_ops; | 545 | out->ops.hdmi = &hdmi_ops; |
546 | out->owner = THIS_MODULE; | 546 | out->owner = THIS_MODULE; |
547 | 547 | ||
548 | omapdss_register_output(out); | 548 | omapdss_register_output(out); |
549 | } | 549 | } |
550 | 550 | ||
551 | static void __exit hdmi_uninit_output(struct platform_device *pdev) | 551 | static void __exit hdmi_uninit_output(struct platform_device *pdev) |
552 | { | 552 | { |
553 | struct omap_dss_device *out = &hdmi.output; | 553 | struct omap_dss_device *out = &hdmi.output; |
554 | 554 | ||
555 | omapdss_unregister_output(out); | 555 | omapdss_unregister_output(out); |
556 | } | 556 | } |
557 | 557 | ||
558 | static int hdmi_probe_of(struct platform_device *pdev) | 558 | static int hdmi_probe_of(struct platform_device *pdev) |
559 | { | 559 | { |
560 | struct device_node *node = pdev->dev.of_node; | 560 | struct device_node *node = pdev->dev.of_node; |
561 | struct device_node *ep; | 561 | struct device_node *ep; |
562 | int r; | 562 | int r; |
563 | 563 | ||
564 | ep = omapdss_of_get_first_endpoint(node); | 564 | ep = omapdss_of_get_first_endpoint(node); |
565 | if (!ep) | 565 | if (!ep) |
566 | return 0; | 566 | return 0; |
567 | 567 | ||
568 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | 568 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); |
569 | if (r) | 569 | if (r) |
570 | goto err; | 570 | goto err; |
571 | 571 | ||
572 | of_node_put(ep); | 572 | of_node_put(ep); |
573 | return 0; | 573 | return 0; |
574 | 574 | ||
575 | err: | 575 | err: |
576 | of_node_put(ep); | 576 | of_node_put(ep); |
577 | return r; | 577 | return r; |
578 | } | 578 | } |
579 | 579 | ||
580 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | 580 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) |
581 | static int audio_startup(struct device *dev, | 581 | static int audio_startup(struct device *dev, |
582 | void (*abort_cb)(struct device *dev)) | 582 | void (*abort_cb)(struct device *dev)) |
583 | { | 583 | { |
584 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 584 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
585 | int ret = 0; | 585 | int ret = 0; |
586 | 586 | ||
587 | mutex_lock(&hd->lock); | 587 | mutex_lock(&hd->lock); |
588 | 588 | ||
589 | if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { | 589 | if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { |
590 | ret = -EPERM; | 590 | ret = -EPERM; |
591 | goto out; | 591 | goto out; |
592 | } | 592 | } |
593 | 593 | ||
594 | hd->audio_abort_cb = abort_cb; | 594 | hd->audio_abort_cb = abort_cb; |
595 | 595 | ||
596 | out: | 596 | out: |
597 | mutex_unlock(&hd->lock); | 597 | mutex_unlock(&hd->lock); |
598 | 598 | ||
599 | return ret; | 599 | return ret; |
600 | } | 600 | } |
601 | 601 | ||
602 | static int audio_enable(struct device *dev, bool enable) | 602 | static int audio_enable(struct device *dev, bool enable) |
603 | { | 603 | { |
604 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 604 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
605 | int ret; | 605 | int ret; |
606 | 606 | ||
607 | mutex_lock(&hd->lock); | 607 | mutex_lock(&hd->lock); |
608 | 608 | ||
609 | ret = hdmi_wp_audio_enable(&hd->wp, enable); | 609 | ret = hdmi_wp_audio_enable(&hd->wp, enable); |
610 | 610 | ||
611 | if (!enable) | 611 | if (!enable) |
612 | hd->audio_abort_cb = NULL; | 612 | hd->audio_abort_cb = NULL; |
613 | 613 | ||
614 | mutex_unlock(&hd->lock); | 614 | mutex_unlock(&hd->lock); |
615 | 615 | ||
616 | return ret; | 616 | return ret; |
617 | } | 617 | } |
618 | 618 | ||
619 | static int audio_start(struct device *dev, bool enable) | 619 | static int audio_start(struct device *dev, bool enable) |
620 | { | 620 | { |
621 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 621 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
622 | int ret = 0; | 622 | int ret = 0; |
623 | 623 | ||
624 | if (enable) | 624 | if (enable) |
625 | ret = hdmi4_audio_start(&hd->core, &hd->wp); | 625 | ret = hdmi4_audio_start(&hd->core, &hd->wp); |
626 | else | 626 | else |
627 | hdmi4_audio_stop(&hd->core, &hd->wp); | 627 | hdmi4_audio_stop(&hd->core, &hd->wp); |
628 | 628 | ||
629 | return ret; | 629 | return ret; |
630 | } | 630 | } |
631 | 631 | ||
632 | static int audio_config(struct device *dev, struct omap_dss_audio *dss_audio) | 632 | static int audio_config(struct device *dev, struct omap_dss_audio *dss_audio) |
633 | { | 633 | { |
634 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 634 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
635 | int ret; | 635 | int ret; |
636 | 636 | ||
637 | mutex_lock(&hd->lock); | 637 | mutex_lock(&hd->lock); |
638 | if (!hdmi_mode_has_audio(&hd->cfg)) | 638 | if (!hdmi_mode_has_audio(&hd->cfg)) |
639 | ret = -EPERM; | 639 | ret = -EPERM; |
640 | else | 640 | else |
641 | ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, | 641 | ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, |
642 | hd->cfg.timings.pixelclock); | 642 | hd->cfg.timings.pixelclock); |
643 | mutex_unlock(&hd->lock); | 643 | mutex_unlock(&hd->lock); |
644 | 644 | ||
645 | return ret; | 645 | return ret; |
646 | } | 646 | } |
647 | 647 | ||
648 | static int hdmi_audio_register(struct device *dev) | 648 | static int hdmi_audio_register(struct device *dev) |
649 | { | 649 | { |
650 | struct omap_hdmi_audio ha = { | 650 | struct omap_hdmi_audio ha = { |
651 | .dev = dev, | 651 | .dev = dev, |
652 | .hw_version = OMAP4_HDMI, | 652 | .hw_version = OMAP4_HDMI, |
653 | .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), | 653 | .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), |
654 | .audio_startup = audio_startup, | 654 | .audio_startup = audio_startup, |
655 | .audio_enable = audio_enable, | 655 | .audio_enable = audio_enable, |
656 | .audio_start = audio_start, | 656 | .audio_start = audio_start, |
657 | .audio_config = audio_config, | 657 | .audio_config = audio_config, |
658 | }; | 658 | }; |
659 | 659 | ||
660 | return omap_hdmi_audio_register(&ha); | 660 | return omap_hdmi_audio_register(&ha); |
661 | } | 661 | } |
662 | #else /* CONFIG_OMAP4_DSS_HDMI_AUDIO */ | 662 | #else /* CONFIG_OMAP4_DSS_HDMI_AUDIO */ |
663 | static int hdmi_audio_register(struct device *dev) | 663 | static int hdmi_audio_register(struct device *dev) |
664 | { | 664 | { |
665 | return 0; | 665 | return 0; |
666 | } | 666 | } |
667 | #endif | 667 | #endif |
668 | 668 | ||
669 | /* HDMI HW IP initialisation */ | 669 | /* HDMI HW IP initialisation */ |
670 | static int omapdss_hdmihw_probe(struct platform_device *pdev) | 670 | static int omapdss_hdmihw_probe(struct platform_device *pdev) |
671 | { | 671 | { |
672 | int r; | 672 | int r; |
673 | int irq; | 673 | int irq; |
674 | 674 | ||
675 | hdmi.pdev = pdev; | 675 | hdmi.pdev = pdev; |
676 | dev_set_drvdata(&pdev->dev, &hdmi); | 676 | dev_set_drvdata(&pdev->dev, &hdmi); |
677 | 677 | ||
678 | mutex_init(&hdmi.lock); | 678 | mutex_init(&hdmi.lock); |
679 | 679 | ||
680 | if (pdev->dev.of_node) { | 680 | if (pdev->dev.of_node) { |
681 | r = hdmi_probe_of(pdev); | 681 | r = hdmi_probe_of(pdev); |
682 | if (r) | 682 | if (r) |
683 | return r; | 683 | return r; |
684 | } | 684 | } |
685 | 685 | ||
686 | r = hdmi_wp_init(pdev, &hdmi.wp); | 686 | r = hdmi_wp_init(pdev, &hdmi.wp); |
687 | if (r) | 687 | if (r) |
688 | return r; | 688 | return r; |
689 | 689 | ||
690 | hdmi.pll = pll_create(pdev, "pll", "sys_clk", DSS_PLL_TYPE_HDMI, | 690 | hdmi.pll = pll_create(pdev, "pll", "sys_clk", DSS_PLL_TYPE_HDMI, |
691 | NULL, 0); | 691 | NULL, 0); |
692 | if (!hdmi.pll) | 692 | if (!hdmi.pll) |
693 | return -ENODEV; | 693 | return -ENODEV; |
694 | 694 | ||
695 | r = hdmi_phy_init(pdev, &hdmi.phy); | 695 | r = hdmi_phy_init(pdev, &hdmi.phy); |
696 | if (r) | 696 | if (r) |
697 | return r; | 697 | return r; |
698 | 698 | ||
699 | r = hdmi4_core_init(pdev, &hdmi.core); | 699 | r = hdmi4_core_init(pdev, &hdmi.core); |
700 | if (r) | 700 | if (r) |
701 | return r; | 701 | return r; |
702 | 702 | ||
703 | irq = platform_get_irq(pdev, 0); | 703 | irq = platform_get_irq(pdev, 0); |
704 | if (irq < 0) { | 704 | if (irq < 0) { |
705 | DSSERR("platform_get_irq failed\n"); | 705 | DSSERR("platform_get_irq failed\n"); |
706 | return -ENODEV; | 706 | return -ENODEV; |
707 | } | 707 | } |
708 | 708 | ||
709 | r = devm_request_threaded_irq(&pdev->dev, irq, | 709 | r = devm_request_threaded_irq(&pdev->dev, irq, |
710 | NULL, hdmi_irq_handler, | 710 | NULL, hdmi_irq_handler, |
711 | IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); | 711 | IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); |
712 | if (r) { | 712 | if (r) { |
713 | DSSERR("HDMI IRQ request failed\n"); | 713 | DSSERR("HDMI IRQ request failed\n"); |
714 | return r; | 714 | return r; |
715 | } | 715 | } |
716 | 716 | ||
717 | pm_runtime_enable(&pdev->dev); | 717 | pm_runtime_enable(&pdev->dev); |
718 | 718 | ||
719 | hdmi_init_output(pdev); | 719 | hdmi_init_output(pdev); |
720 | 720 | ||
721 | r = hdmi_audio_register(&pdev->dev); | 721 | r = hdmi_audio_register(&pdev->dev); |
722 | if (r) { | 722 | if (r) { |
723 | DSSERR("Registering HDMI audio failed\n"); | 723 | DSSERR("Registering HDMI audio failed\n"); |
724 | hdmi_uninit_output(pdev); | 724 | hdmi_uninit_output(pdev); |
725 | pm_runtime_disable(&pdev->dev); | 725 | pm_runtime_disable(&pdev->dev); |
726 | return r; | 726 | return r; |
727 | } | 727 | } |
728 | 728 | ||
729 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); | 729 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); |
730 | 730 | ||
731 | return 0; | 731 | return 0; |
732 | } | 732 | } |
733 | 733 | ||
734 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) | 734 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) |
735 | { | 735 | { |
736 | omap_hdmi_audio_unregister(&pdev->dev); | 736 | omap_hdmi_audio_unregister(&pdev->dev); |
737 | 737 | ||
738 | hdmi_uninit_output(pdev); | 738 | hdmi_uninit_output(pdev); |
739 | 739 | ||
740 | pm_runtime_disable(&pdev->dev); | 740 | pm_runtime_disable(&pdev->dev); |
741 | 741 | ||
742 | return 0; | 742 | return 0; |
743 | } | 743 | } |
744 | 744 | ||
745 | static int hdmi_runtime_suspend(struct device *dev) | 745 | static int hdmi_runtime_suspend(struct device *dev) |
746 | { | 746 | { |
747 | pll_enable_clock(hdmi.pll, false); | 747 | pll_enable_clock(hdmi.pll, false); |
748 | 748 | ||
749 | dispc_runtime_put(); | 749 | dispc_runtime_put(); |
750 | 750 | ||
751 | return 0; | 751 | return 0; |
752 | } | 752 | } |
753 | 753 | ||
754 | static int hdmi_runtime_resume(struct device *dev) | 754 | static int hdmi_runtime_resume(struct device *dev) |
755 | { | 755 | { |
756 | int r; | 756 | int r; |
757 | 757 | ||
758 | r = dispc_runtime_get(); | 758 | r = dispc_runtime_get(); |
759 | if (r < 0) | 759 | if (r < 0) |
760 | return r; | 760 | return r; |
761 | 761 | ||
762 | pll_enable_clock(hdmi.pll, true); | 762 | pll_enable_clock(hdmi.pll, true); |
763 | 763 | ||
764 | return 0; | 764 | return 0; |
765 | } | 765 | } |
766 | 766 | ||
767 | static const struct dev_pm_ops hdmi_pm_ops = { | 767 | static const struct dev_pm_ops hdmi_pm_ops = { |
768 | .runtime_suspend = hdmi_runtime_suspend, | 768 | .runtime_suspend = hdmi_runtime_suspend, |
769 | .runtime_resume = hdmi_runtime_resume, | 769 | .runtime_resume = hdmi_runtime_resume, |
770 | }; | 770 | }; |
771 | 771 | ||
772 | static const struct of_device_id hdmi_of_match[] = { | 772 | static const struct of_device_id hdmi_of_match[] = { |
773 | { .compatible = "ti,omap4-hdmi", }, | 773 | { .compatible = "ti,omap4-hdmi", }, |
774 | {}, | 774 | {}, |
775 | }; | 775 | }; |
776 | 776 | ||
777 | static struct platform_driver omapdss_hdmihw_driver = { | 777 | static struct platform_driver omapdss_hdmihw_driver = { |
778 | .probe = omapdss_hdmihw_probe, | 778 | .probe = omapdss_hdmihw_probe, |
779 | .remove = __exit_p(omapdss_hdmihw_remove), | 779 | .remove = __exit_p(omapdss_hdmihw_remove), |
780 | .driver = { | 780 | .driver = { |
781 | .name = "omapdss_hdmi", | 781 | .name = "omapdss_hdmi", |
782 | .owner = THIS_MODULE, | 782 | .owner = THIS_MODULE, |
783 | .pm = &hdmi_pm_ops, | 783 | .pm = &hdmi_pm_ops, |
784 | .of_match_table = hdmi_of_match, | 784 | .of_match_table = hdmi_of_match, |
785 | }, | 785 | }, |
786 | }; | 786 | }; |
787 | 787 | ||
788 | int __init hdmi4_init_platform_driver(void) | 788 | int __init hdmi4_init_platform_driver(void) |
789 | { | 789 | { |
790 | return platform_driver_register(&omapdss_hdmihw_driver); | 790 | return platform_driver_register(&omapdss_hdmihw_driver); |
791 | } | 791 | } |
792 | 792 | ||
793 | void __exit hdmi4_uninit_platform_driver(void) | 793 | void __exit hdmi4_uninit_platform_driver(void) |
794 | { | 794 | { |
795 | platform_driver_unregister(&omapdss_hdmihw_driver); | 795 | platform_driver_unregister(&omapdss_hdmihw_driver); |
796 | } | 796 | } |
797 | 797 |
drivers/video/fbdev/omap2/dss/hdmi5.c
1 | /* | 1 | /* |
2 | * HDMI driver for OMAP5 | 2 | * HDMI driver for OMAP5 |
3 | * | 3 | * |
4 | * Copyright (C) 2014 Texas Instruments Incorporated | 4 | * Copyright (C) 2014 Texas Instruments Incorporated |
5 | * | 5 | * |
6 | * Authors: | 6 | * Authors: |
7 | * Yong Zhi | 7 | * Yong Zhi |
8 | * Mythri pk | 8 | * Mythri pk |
9 | * Archit Taneja <archit@ti.com> | 9 | * Archit Taneja <archit@ti.com> |
10 | * Tomi Valkeinen <tomi.valkeinen@ti.com> | 10 | * Tomi Valkeinen <tomi.valkeinen@ti.com> |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or modify it | 12 | * This program is free software; you can redistribute it and/or modify it |
13 | * under the terms of the GNU General Public License version 2 as published by | 13 | * under the terms of the GNU General Public License version 2 as published by |
14 | * the Free Software Foundation. | 14 | * the Free Software Foundation. |
15 | * | 15 | * |
16 | * This program is distributed in the hope that it will be useful, but WITHOUT | 16 | * This program is distributed in the hope that it will be useful, but WITHOUT |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
19 | * more details. | 19 | * more details. |
20 | * | 20 | * |
21 | * You should have received a copy of the GNU General Public License along with | 21 | * You should have received a copy of the GNU General Public License along with |
22 | * this program. If not, see <http://www.gnu.org/licenses/>. | 22 | * this program. If not, see <http://www.gnu.org/licenses/>. |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #define DSS_SUBSYS_NAME "HDMI" | 25 | #define DSS_SUBSYS_NAME "HDMI" |
26 | 26 | ||
27 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
29 | #include <linux/err.h> | 29 | #include <linux/err.h> |
30 | #include <linux/io.h> | 30 | #include <linux/io.h> |
31 | #include <linux/interrupt.h> | 31 | #include <linux/interrupt.h> |
32 | #include <linux/mutex.h> | 32 | #include <linux/mutex.h> |
33 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
34 | #include <linux/string.h> | 34 | #include <linux/string.h> |
35 | #include <linux/platform_device.h> | 35 | #include <linux/platform_device.h> |
36 | #include <linux/pm_runtime.h> | 36 | #include <linux/pm_runtime.h> |
37 | #include <linux/clk.h> | 37 | #include <linux/clk.h> |
38 | #include <linux/gpio.h> | 38 | #include <linux/gpio.h> |
39 | #include <linux/regulator/consumer.h> | 39 | #include <linux/regulator/consumer.h> |
40 | #include <video/omapdss.h> | 40 | #include <video/omapdss.h> |
41 | #include <sound/omap-hdmi-audio.h> | 41 | #include <sound/omap-hdmi-audio.h> |
42 | 42 | ||
43 | #include "hdmi5_core.h" | 43 | #include "hdmi5_core.h" |
44 | #include "dss.h" | 44 | #include "dss.h" |
45 | #include "dss_features.h" | 45 | #include "dss_features.h" |
46 | 46 | ||
47 | static struct omap_hdmi hdmi; | 47 | static struct omap_hdmi hdmi; |
48 | 48 | ||
49 | static int hdmi_runtime_get(void) | 49 | static int hdmi_runtime_get(void) |
50 | { | 50 | { |
51 | int r; | 51 | int r; |
52 | 52 | ||
53 | DSSDBG("hdmi_runtime_get\n"); | 53 | DSSDBG("hdmi_runtime_get\n"); |
54 | 54 | ||
55 | r = pm_runtime_get_sync(&hdmi.pdev->dev); | 55 | r = pm_runtime_get_sync(&hdmi.pdev->dev); |
56 | WARN_ON(r < 0); | 56 | WARN_ON(r < 0); |
57 | if (r < 0) | 57 | if (r < 0) |
58 | return r; | 58 | return r; |
59 | 59 | ||
60 | return 0; | 60 | return 0; |
61 | } | 61 | } |
62 | 62 | ||
63 | static void hdmi_runtime_put(void) | 63 | static void hdmi_runtime_put(void) |
64 | { | 64 | { |
65 | int r; | 65 | int r; |
66 | 66 | ||
67 | DSSDBG("hdmi_runtime_put\n"); | 67 | DSSDBG("hdmi_runtime_put\n"); |
68 | 68 | ||
69 | r = pm_runtime_put_sync(&hdmi.pdev->dev); | 69 | r = pm_runtime_put_sync(&hdmi.pdev->dev); |
70 | WARN_ON(r < 0 && r != -ENOSYS); | 70 | WARN_ON(r < 0 && r != -ENOSYS); |
71 | } | 71 | } |
72 | 72 | ||
73 | static irqreturn_t hdmi_irq_handler(int irq, void *data) | 73 | static irqreturn_t hdmi_irq_handler(int irq, void *data) |
74 | { | 74 | { |
75 | struct hdmi_wp_data *wp = data; | 75 | struct hdmi_wp_data *wp = data; |
76 | u32 irqstatus; | 76 | u32 irqstatus; |
77 | 77 | ||
78 | irqstatus = hdmi_wp_get_irqstatus(wp); | 78 | irqstatus = hdmi_wp_get_irqstatus(wp); |
79 | hdmi_wp_set_irqstatus(wp, irqstatus); | 79 | hdmi_wp_set_irqstatus(wp, irqstatus); |
80 | 80 | ||
81 | if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && | 81 | if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && |
82 | irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | 82 | irqstatus & HDMI_IRQ_LINK_DISCONNECT) { |
83 | u32 v; | 83 | u32 v; |
84 | /* | 84 | /* |
85 | * If we get both connect and disconnect interrupts at the same | 85 | * If we get both connect and disconnect interrupts at the same |
86 | * time, turn off the PHY, clear interrupts, and restart, which | 86 | * time, turn off the PHY, clear interrupts, and restart, which |
87 | * raises connect interrupt if a cable is connected, or nothing | 87 | * raises connect interrupt if a cable is connected, or nothing |
88 | * if cable is not connected. | 88 | * if cable is not connected. |
89 | */ | 89 | */ |
90 | 90 | ||
91 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); | 91 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); |
92 | 92 | ||
93 | /* | 93 | /* |
94 | * We always get bogus CONNECT & DISCONNECT interrupts when | 94 | * We always get bogus CONNECT & DISCONNECT interrupts when |
95 | * setting the PHY to LDOON. To ignore those, we force the RXDET | 95 | * setting the PHY to LDOON. To ignore those, we force the RXDET |
96 | * line to 0 until the PHY power state has been changed. | 96 | * line to 0 until the PHY power state has been changed. |
97 | */ | 97 | */ |
98 | v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL); | 98 | v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL); |
99 | v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */ | 99 | v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */ |
100 | v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */ | 100 | v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */ |
101 | hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); | 101 | hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); |
102 | 102 | ||
103 | hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | | 103 | hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | |
104 | HDMI_IRQ_LINK_DISCONNECT); | 104 | HDMI_IRQ_LINK_DISCONNECT); |
105 | 105 | ||
106 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | 106 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
107 | 107 | ||
108 | REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); | 108 | REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); |
109 | 109 | ||
110 | } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { | 110 | } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { |
111 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); | 111 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); |
112 | } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | 112 | } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { |
113 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | 113 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
114 | } | 114 | } |
115 | 115 | ||
116 | return IRQ_HANDLED; | 116 | return IRQ_HANDLED; |
117 | } | 117 | } |
118 | 118 | ||
119 | static int hdmi_init_regulator(void) | 119 | static int hdmi_init_regulator(void) |
120 | { | 120 | { |
121 | int r; | 121 | int r; |
122 | struct regulator *reg; | 122 | struct regulator *reg; |
123 | 123 | ||
124 | if (hdmi.vdda_reg != NULL) | 124 | if (hdmi.vdda_reg != NULL) |
125 | return 0; | 125 | return 0; |
126 | 126 | ||
127 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); | 127 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); |
128 | if (IS_ERR(reg)) { | 128 | if (IS_ERR(reg)) { |
129 | DSSERR("can't get VDDA regulator\n"); | 129 | DSSERR("can't get VDDA regulator\n"); |
130 | return PTR_ERR(reg); | 130 | return PTR_ERR(reg); |
131 | } | 131 | } |
132 | 132 | ||
133 | if (regulator_can_change_voltage(reg)) { | 133 | if (regulator_can_change_voltage(reg)) { |
134 | r = regulator_set_voltage(reg, 1800000, 1800000); | 134 | r = regulator_set_voltage(reg, 1800000, 1800000); |
135 | if (r) { | 135 | if (r) { |
136 | devm_regulator_put(reg); | 136 | devm_regulator_put(reg); |
137 | DSSWARN("can't set the regulator voltage\n"); | 137 | DSSWARN("can't set the regulator voltage\n"); |
138 | return r; | 138 | return r; |
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | hdmi.vdda_reg = reg; | 142 | hdmi.vdda_reg = reg; |
143 | 143 | ||
144 | return 0; | 144 | return 0; |
145 | } | 145 | } |
146 | 146 | ||
147 | static int hdmi_pll_enable(struct pll_data *pll, struct hdmi_wp_data *wp, | 147 | static int hdmi_pll_enable(struct pll_data *pll, struct hdmi_wp_data *wp, |
148 | struct pll_params *p) | 148 | struct pll_params *p) |
149 | { | 149 | { |
150 | u16 r = 0; | 150 | u16 r = 0; |
151 | 151 | ||
152 | dss_ctrl_hdmi_pll_enable(true); | 152 | dss_ctrl_hdmi_pll_enable(true); |
153 | 153 | ||
154 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | 154 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
155 | if (r) | 155 | if (r) |
156 | return r; | 156 | return r; |
157 | 157 | ||
158 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); | 158 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); |
159 | if (r) | 159 | if (r) |
160 | return r; | 160 | return r; |
161 | 161 | ||
162 | pll_sysreset(pll); | 162 | pll_sysreset(pll); |
163 | 163 | ||
164 | r = pll_wait_reset(pll); | 164 | r = pll_wait_reset(pll); |
165 | if (r) | 165 | if (r) |
166 | return r; | 166 | return r; |
167 | 167 | ||
168 | r = pll_calc_and_check_clock_rates(pll, p); | 168 | r = pll_calc_and_check_clock_rates(pll, p); |
169 | if (r) | 169 | if (r) |
170 | return r; | 170 | return r; |
171 | 171 | ||
172 | r = pll_set_clock_div(pll, p); | 172 | r = pll_set_clock_div(pll, p); |
173 | if (r) | 173 | if (r) |
174 | return r; | 174 | return r; |
175 | 175 | ||
176 | return 0; | 176 | return 0; |
177 | } | 177 | } |
178 | 178 | ||
179 | static void hdmi_pll_disable(struct hdmi_wp_data *wp) | 179 | static void hdmi_pll_disable(struct hdmi_wp_data *wp) |
180 | { | 180 | { |
181 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | 181 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
182 | 182 | ||
183 | dss_ctrl_hdmi_pll_enable(false); | 183 | dss_ctrl_hdmi_pll_enable(false); |
184 | } | 184 | } |
185 | 185 | ||
186 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) | 186 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) |
187 | { | 187 | { |
188 | int r; | 188 | int r; |
189 | 189 | ||
190 | r = regulator_enable(hdmi.vdda_reg); | 190 | r = regulator_enable(hdmi.vdda_reg); |
191 | if (r) | 191 | if (r) |
192 | return r; | 192 | return r; |
193 | 193 | ||
194 | r = hdmi_runtime_get(); | 194 | r = hdmi_runtime_get(); |
195 | if (r) | 195 | if (r) |
196 | goto err_runtime_get; | 196 | goto err_runtime_get; |
197 | 197 | ||
198 | /* Make selection of HDMI in DSS */ | 198 | /* Make selection of HDMI in DSS */ |
199 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | 199 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); |
200 | 200 | ||
201 | hdmi.core_enabled = true; | 201 | hdmi.core_enabled = true; |
202 | 202 | ||
203 | return 0; | 203 | return 0; |
204 | 204 | ||
205 | err_runtime_get: | 205 | err_runtime_get: |
206 | regulator_disable(hdmi.vdda_reg); | 206 | regulator_disable(hdmi.vdda_reg); |
207 | 207 | ||
208 | return r; | 208 | return r; |
209 | } | 209 | } |
210 | 210 | ||
211 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) | 211 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) |
212 | { | 212 | { |
213 | hdmi.core_enabled = false; | 213 | hdmi.core_enabled = false; |
214 | 214 | ||
215 | hdmi_runtime_put(); | 215 | hdmi_runtime_put(); |
216 | regulator_disable(hdmi.vdda_reg); | 216 | regulator_disable(hdmi.vdda_reg); |
217 | } | 217 | } |
218 | 218 | ||
219 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) | 219 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) |
220 | { | 220 | { |
221 | int r; | 221 | int r; |
222 | struct omap_video_timings *p; | 222 | struct omap_video_timings *p; |
223 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 223 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
224 | unsigned long phy; | 224 | unsigned long phy; |
225 | struct pll_params param; | 225 | struct pll_params param; |
226 | bool ok; | 226 | bool ok; |
227 | 227 | ||
228 | r = hdmi_power_on_core(dssdev); | 228 | r = hdmi_power_on_core(dssdev); |
229 | if (r) | 229 | if (r) |
230 | return r; | 230 | return r; |
231 | 231 | ||
232 | p = &hdmi.cfg.timings; | 232 | p = &hdmi.cfg.timings; |
233 | 233 | ||
234 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | 234 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
235 | 235 | ||
236 | phy = p->pixelclock * 10; | 236 | phy = p->pixelclock * 10; |
237 | 237 | ||
238 | memset(¶m, 0, sizeof(param)); | 238 | memset(¶m, 0, sizeof(param)); |
239 | 239 | ||
240 | ok = pll_calc(hdmi.pll, phy, phy, NULL, ¶m); | 240 | ok = pll_calc(hdmi.pll, phy, phy, NULL, ¶m); |
241 | if (!ok) { | 241 | if (!ok) { |
242 | DSSDBG("Failed to calculate PLL settings\n"); | 242 | DSSDBG("Failed to calculate PLL settings\n"); |
243 | goto err_pll_enable; | 243 | goto err_pll_enable; |
244 | } | 244 | } |
245 | 245 | ||
246 | /* disable and clear irqs */ | 246 | /* disable and clear irqs */ |
247 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | 247 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); |
248 | hdmi_wp_set_irqstatus(&hdmi.wp, | 248 | hdmi_wp_set_irqstatus(&hdmi.wp, |
249 | hdmi_wp_get_irqstatus(&hdmi.wp)); | 249 | hdmi_wp_get_irqstatus(&hdmi.wp)); |
250 | 250 | ||
251 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ | 251 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ |
252 | r = hdmi_pll_enable(hdmi.pll, &hdmi.wp, ¶m); | 252 | r = hdmi_pll_enable(hdmi.pll, &hdmi.wp, ¶m); |
253 | if (r) { | 253 | if (r) { |
254 | DSSDBG("Failed to lock PLL\n"); | 254 | DSSDBG("Failed to lock PLL\n"); |
255 | goto err_pll_enable; | 255 | goto err_pll_enable; |
256 | } | 256 | } |
257 | 257 | ||
258 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | 258 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); |
259 | if (r) { | 259 | if (r) { |
260 | DSSDBG("Failed to start PHY\n"); | 260 | DSSDBG("Failed to start PHY\n"); |
261 | goto err_phy_cfg; | 261 | goto err_phy_cfg; |
262 | } | 262 | } |
263 | 263 | ||
264 | r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON); | 264 | r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON); |
265 | if (r) | 265 | if (r) |
266 | goto err_phy_pwr; | 266 | goto err_phy_pwr; |
267 | 267 | ||
268 | hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); | 268 | hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); |
269 | 269 | ||
270 | /* bypass TV gamma table */ | 270 | /* bypass TV gamma table */ |
271 | dispc_enable_gamma_table(0); | 271 | dispc_enable_gamma_table(0); |
272 | 272 | ||
273 | /* tv size */ | 273 | /* tv size */ |
274 | dss_mgr_set_timings(mgr, p); | 274 | dss_mgr_set_timings(mgr, p); |
275 | 275 | ||
276 | r = dss_mgr_enable(mgr); | 276 | r = dss_mgr_enable(mgr); |
277 | if (r) | 277 | if (r) |
278 | goto err_mgr_enable; | 278 | goto err_mgr_enable; |
279 | 279 | ||
280 | r = hdmi_wp_video_start(&hdmi.wp); | 280 | r = hdmi_wp_video_start(&hdmi.wp); |
281 | if (r) | 281 | if (r) |
282 | goto err_vid_enable; | 282 | goto err_vid_enable; |
283 | 283 | ||
284 | hdmi_wp_set_irqenable(&hdmi.wp, | 284 | hdmi_wp_set_irqenable(&hdmi.wp, |
285 | HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); | 285 | HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); |
286 | 286 | ||
287 | return 0; | 287 | return 0; |
288 | 288 | ||
289 | err_vid_enable: | 289 | err_vid_enable: |
290 | dss_mgr_disable(mgr); | 290 | dss_mgr_disable(mgr); |
291 | err_mgr_enable: | 291 | err_mgr_enable: |
292 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | 292 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); |
293 | err_phy_pwr: | 293 | err_phy_pwr: |
294 | err_phy_cfg: | 294 | err_phy_cfg: |
295 | hdmi_pll_disable(&hdmi.wp); | 295 | hdmi_pll_disable(&hdmi.wp); |
296 | err_pll_enable: | 296 | err_pll_enable: |
297 | hdmi_power_off_core(dssdev); | 297 | hdmi_power_off_core(dssdev); |
298 | return -EIO; | 298 | return -EIO; |
299 | } | 299 | } |
300 | 300 | ||
301 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) | 301 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) |
302 | { | 302 | { |
303 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 303 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
304 | 304 | ||
305 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | 305 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); |
306 | 306 | ||
307 | hdmi_wp_video_stop(&hdmi.wp); | 307 | hdmi_wp_video_stop(&hdmi.wp); |
308 | 308 | ||
309 | dss_mgr_disable(mgr); | 309 | dss_mgr_disable(mgr); |
310 | 310 | ||
311 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | 311 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); |
312 | 312 | ||
313 | hdmi_pll_disable(&hdmi.wp); | 313 | hdmi_pll_disable(&hdmi.wp); |
314 | 314 | ||
315 | hdmi_power_off_core(dssdev); | 315 | hdmi_power_off_core(dssdev); |
316 | } | 316 | } |
317 | 317 | ||
318 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, | 318 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, |
319 | struct omap_video_timings *timings) | 319 | struct omap_video_timings *timings) |
320 | { | 320 | { |
321 | struct omap_dss_device *out = &hdmi.output; | 321 | struct omap_dss_device *out = &hdmi.output; |
322 | 322 | ||
323 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) | 323 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) |
324 | return -EINVAL; | 324 | return -EINVAL; |
325 | 325 | ||
326 | return 0; | 326 | return 0; |
327 | } | 327 | } |
328 | 328 | ||
329 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, | 329 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, |
330 | struct omap_video_timings *timings) | 330 | struct omap_video_timings *timings) |
331 | { | 331 | { |
332 | mutex_lock(&hdmi.lock); | 332 | mutex_lock(&hdmi.lock); |
333 | 333 | ||
334 | hdmi.cfg.timings = *timings; | 334 | hdmi.cfg.timings = *timings; |
335 | 335 | ||
336 | dispc_set_tv_pclk(timings->pixelclock); | 336 | dispc_set_tv_pclk(timings->pixelclock); |
337 | 337 | ||
338 | mutex_unlock(&hdmi.lock); | 338 | mutex_unlock(&hdmi.lock); |
339 | } | 339 | } |
340 | 340 | ||
341 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, | 341 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, |
342 | struct omap_video_timings *timings) | 342 | struct omap_video_timings *timings) |
343 | { | 343 | { |
344 | *timings = hdmi.cfg.timings; | 344 | *timings = hdmi.cfg.timings; |
345 | } | 345 | } |
346 | 346 | ||
347 | static void hdmi_dump_regs(struct seq_file *s) | 347 | static void hdmi_dump_regs(struct seq_file *s) |
348 | { | 348 | { |
349 | mutex_lock(&hdmi.lock); | 349 | mutex_lock(&hdmi.lock); |
350 | 350 | ||
351 | if (hdmi_runtime_get()) { | 351 | if (hdmi_runtime_get()) { |
352 | mutex_unlock(&hdmi.lock); | 352 | mutex_unlock(&hdmi.lock); |
353 | return; | 353 | return; |
354 | } | 354 | } |
355 | 355 | ||
356 | hdmi_wp_dump(&hdmi.wp, s); | 356 | hdmi_wp_dump(&hdmi.wp, s); |
357 | pll_dump(hdmi.pll, s); | 357 | pll_dump(hdmi.pll, s); |
358 | hdmi_phy_dump(&hdmi.phy, s); | 358 | hdmi_phy_dump(&hdmi.phy, s); |
359 | hdmi5_core_dump(&hdmi.core, s); | 359 | hdmi5_core_dump(&hdmi.core, s); |
360 | 360 | ||
361 | hdmi_runtime_put(); | 361 | hdmi_runtime_put(); |
362 | mutex_unlock(&hdmi.lock); | 362 | mutex_unlock(&hdmi.lock); |
363 | } | 363 | } |
364 | 364 | ||
365 | static int read_edid(u8 *buf, int len) | 365 | static int read_edid(u8 *buf, int len) |
366 | { | 366 | { |
367 | int r; | 367 | int r; |
368 | int idlemode; | 368 | int idlemode; |
369 | 369 | ||
370 | mutex_lock(&hdmi.lock); | 370 | mutex_lock(&hdmi.lock); |
371 | 371 | ||
372 | r = hdmi_runtime_get(); | 372 | r = hdmi_runtime_get(); |
373 | BUG_ON(r); | 373 | BUG_ON(r); |
374 | 374 | ||
375 | idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); | 375 | idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); |
376 | /* No-idle mode */ | 376 | /* No-idle mode */ |
377 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); | 377 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); |
378 | 378 | ||
379 | r = hdmi5_read_edid(&hdmi.core, buf, len); | 379 | r = hdmi5_read_edid(&hdmi.core, buf, len); |
380 | 380 | ||
381 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); | 381 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); |
382 | 382 | ||
383 | hdmi_runtime_put(); | 383 | hdmi_runtime_put(); |
384 | mutex_unlock(&hdmi.lock); | 384 | mutex_unlock(&hdmi.lock); |
385 | 385 | ||
386 | return r; | 386 | return r; |
387 | } | 387 | } |
388 | 388 | ||
389 | static int hdmi_display_enable(struct omap_dss_device *dssdev) | 389 | static int hdmi_display_enable(struct omap_dss_device *dssdev) |
390 | { | 390 | { |
391 | struct omap_dss_device *out = &hdmi.output; | 391 | struct omap_dss_device *out = &hdmi.output; |
392 | int r = 0; | 392 | int r = 0; |
393 | 393 | ||
394 | DSSDBG("ENTER hdmi_display_enable\n"); | 394 | DSSDBG("ENTER hdmi_display_enable\n"); |
395 | 395 | ||
396 | mutex_lock(&hdmi.lock); | 396 | mutex_lock(&hdmi.lock); |
397 | 397 | ||
398 | if (out == NULL || out->manager == NULL) { | 398 | if (out == NULL || out->manager == NULL) { |
399 | DSSERR("failed to enable display: no output/manager\n"); | 399 | DSSERR("failed to enable display: no output/manager\n"); |
400 | r = -ENODEV; | 400 | r = -ENODEV; |
401 | goto err0; | 401 | goto err0; |
402 | } | 402 | } |
403 | 403 | ||
404 | r = hdmi_power_on_full(dssdev); | 404 | r = hdmi_power_on_full(dssdev); |
405 | if (r) { | 405 | if (r) { |
406 | DSSERR("failed to power on device\n"); | 406 | DSSERR("failed to power on device\n"); |
407 | goto err0; | 407 | goto err0; |
408 | } | 408 | } |
409 | 409 | ||
410 | hdmi.display_enabled = true; | 410 | hdmi.display_enabled = true; |
411 | 411 | ||
412 | mutex_unlock(&hdmi.lock); | 412 | mutex_unlock(&hdmi.lock); |
413 | return 0; | 413 | return 0; |
414 | 414 | ||
415 | err0: | 415 | err0: |
416 | mutex_unlock(&hdmi.lock); | 416 | mutex_unlock(&hdmi.lock); |
417 | return r; | 417 | return r; |
418 | } | 418 | } |
419 | 419 | ||
420 | static void hdmi_display_disable(struct omap_dss_device *dssdev) | 420 | static void hdmi_display_disable(struct omap_dss_device *dssdev) |
421 | { | 421 | { |
422 | DSSDBG("Enter hdmi_display_disable\n"); | 422 | DSSDBG("Enter hdmi_display_disable\n"); |
423 | 423 | ||
424 | mutex_lock(&hdmi.lock); | 424 | mutex_lock(&hdmi.lock); |
425 | 425 | ||
426 | if (hdmi.audio_abort_cb) | 426 | /* If set hdmi.audio_abort_cb(&hdmi.pdev->dev) should be |
427 | hdmi.audio_abort_cb(&hdmi.pdev->dev); | 427 | * called here, if audio abort functionality is needed. */ |
428 | 428 | ||
429 | hdmi_power_off_full(dssdev); | 429 | hdmi_power_off_full(dssdev); |
430 | 430 | ||
431 | hdmi.display_enabled = false; | 431 | hdmi.display_enabled = false; |
432 | 432 | ||
433 | mutex_unlock(&hdmi.lock); | 433 | mutex_unlock(&hdmi.lock); |
434 | } | 434 | } |
435 | 435 | ||
436 | static int hdmi_core_enable(struct omap_dss_device *dssdev) | 436 | static int hdmi_core_enable(struct omap_dss_device *dssdev) |
437 | { | 437 | { |
438 | int r = 0; | 438 | int r = 0; |
439 | 439 | ||
440 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | 440 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); |
441 | 441 | ||
442 | mutex_lock(&hdmi.lock); | 442 | mutex_lock(&hdmi.lock); |
443 | 443 | ||
444 | r = hdmi_power_on_core(dssdev); | 444 | r = hdmi_power_on_core(dssdev); |
445 | if (r) { | 445 | if (r) { |
446 | DSSERR("failed to power on device\n"); | 446 | DSSERR("failed to power on device\n"); |
447 | goto err0; | 447 | goto err0; |
448 | } | 448 | } |
449 | 449 | ||
450 | mutex_unlock(&hdmi.lock); | 450 | mutex_unlock(&hdmi.lock); |
451 | return 0; | 451 | return 0; |
452 | 452 | ||
453 | err0: | 453 | err0: |
454 | mutex_unlock(&hdmi.lock); | 454 | mutex_unlock(&hdmi.lock); |
455 | return r; | 455 | return r; |
456 | } | 456 | } |
457 | 457 | ||
458 | static void hdmi_core_disable(struct omap_dss_device *dssdev) | 458 | static void hdmi_core_disable(struct omap_dss_device *dssdev) |
459 | { | 459 | { |
460 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); | 460 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); |
461 | 461 | ||
462 | mutex_lock(&hdmi.lock); | 462 | mutex_lock(&hdmi.lock); |
463 | 463 | ||
464 | hdmi_power_off_core(dssdev); | 464 | hdmi_power_off_core(dssdev); |
465 | 465 | ||
466 | mutex_unlock(&hdmi.lock); | 466 | mutex_unlock(&hdmi.lock); |
467 | } | 467 | } |
468 | 468 | ||
469 | static int hdmi_connect(struct omap_dss_device *dssdev, | 469 | static int hdmi_connect(struct omap_dss_device *dssdev, |
470 | struct omap_dss_device *dst) | 470 | struct omap_dss_device *dst) |
471 | { | 471 | { |
472 | struct omap_overlay_manager *mgr; | 472 | struct omap_overlay_manager *mgr; |
473 | int r; | 473 | int r; |
474 | 474 | ||
475 | r = hdmi_init_regulator(); | 475 | r = hdmi_init_regulator(); |
476 | if (r) | 476 | if (r) |
477 | return r; | 477 | return r; |
478 | 478 | ||
479 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | 479 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); |
480 | if (!mgr) | 480 | if (!mgr) |
481 | return -ENODEV; | 481 | return -ENODEV; |
482 | 482 | ||
483 | r = dss_mgr_connect(mgr, dssdev); | 483 | r = dss_mgr_connect(mgr, dssdev); |
484 | if (r) | 484 | if (r) |
485 | return r; | 485 | return r; |
486 | 486 | ||
487 | r = omapdss_output_set_device(dssdev, dst); | 487 | r = omapdss_output_set_device(dssdev, dst); |
488 | if (r) { | 488 | if (r) { |
489 | DSSERR("failed to connect output to new device: %s\n", | 489 | DSSERR("failed to connect output to new device: %s\n", |
490 | dst->name); | 490 | dst->name); |
491 | dss_mgr_disconnect(mgr, dssdev); | 491 | dss_mgr_disconnect(mgr, dssdev); |
492 | return r; | 492 | return r; |
493 | } | 493 | } |
494 | 494 | ||
495 | return 0; | 495 | return 0; |
496 | } | 496 | } |
497 | 497 | ||
498 | static void hdmi_disconnect(struct omap_dss_device *dssdev, | 498 | static void hdmi_disconnect(struct omap_dss_device *dssdev, |
499 | struct omap_dss_device *dst) | 499 | struct omap_dss_device *dst) |
500 | { | 500 | { |
501 | WARN_ON(dst != dssdev->dst); | 501 | WARN_ON(dst != dssdev->dst); |
502 | 502 | ||
503 | if (dst != dssdev->dst) | 503 | if (dst != dssdev->dst) |
504 | return; | 504 | return; |
505 | 505 | ||
506 | omapdss_output_unset_device(dssdev); | 506 | omapdss_output_unset_device(dssdev); |
507 | 507 | ||
508 | if (dssdev->manager) | 508 | if (dssdev->manager) |
509 | dss_mgr_disconnect(dssdev->manager, dssdev); | 509 | dss_mgr_disconnect(dssdev->manager, dssdev); |
510 | } | 510 | } |
511 | 511 | ||
512 | static int hdmi_read_edid(struct omap_dss_device *dssdev, | 512 | static int hdmi_read_edid(struct omap_dss_device *dssdev, |
513 | u8 *edid, int len) | 513 | u8 *edid, int len) |
514 | { | 514 | { |
515 | bool need_enable; | 515 | bool need_enable; |
516 | int r; | 516 | int r; |
517 | 517 | ||
518 | need_enable = hdmi.core_enabled == false; | 518 | need_enable = hdmi.core_enabled == false; |
519 | 519 | ||
520 | if (need_enable) { | 520 | if (need_enable) { |
521 | r = hdmi_core_enable(dssdev); | 521 | r = hdmi_core_enable(dssdev); |
522 | if (r) | 522 | if (r) |
523 | return r; | 523 | return r; |
524 | } | 524 | } |
525 | 525 | ||
526 | r = read_edid(edid, len); | 526 | r = read_edid(edid, len); |
527 | 527 | ||
528 | if (need_enable) | 528 | if (need_enable) |
529 | hdmi_core_disable(dssdev); | 529 | hdmi_core_disable(dssdev); |
530 | 530 | ||
531 | return r; | 531 | return r; |
532 | } | 532 | } |
533 | 533 | ||
534 | static int hdmi_set_infoframe(struct omap_dss_device *dssdev, | 534 | static int hdmi_set_infoframe(struct omap_dss_device *dssdev, |
535 | const struct hdmi_avi_infoframe *avi) | 535 | const struct hdmi_avi_infoframe *avi) |
536 | { | 536 | { |
537 | hdmi.cfg.infoframe = *avi; | 537 | hdmi.cfg.infoframe = *avi; |
538 | return 0; | 538 | return 0; |
539 | } | 539 | } |
540 | 540 | ||
541 | static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, | 541 | static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, |
542 | bool hdmi_mode) | 542 | bool hdmi_mode) |
543 | { | 543 | { |
544 | hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; | 544 | hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; |
545 | return 0; | 545 | return 0; |
546 | } | 546 | } |
547 | 547 | ||
548 | static const struct omapdss_hdmi_ops hdmi_ops = { | 548 | static const struct omapdss_hdmi_ops hdmi_ops = { |
549 | .connect = hdmi_connect, | 549 | .connect = hdmi_connect, |
550 | .disconnect = hdmi_disconnect, | 550 | .disconnect = hdmi_disconnect, |
551 | 551 | ||
552 | .enable = hdmi_display_enable, | 552 | .enable = hdmi_display_enable, |
553 | .disable = hdmi_display_disable, | 553 | .disable = hdmi_display_disable, |
554 | 554 | ||
555 | .check_timings = hdmi_display_check_timing, | 555 | .check_timings = hdmi_display_check_timing, |
556 | .set_timings = hdmi_display_set_timing, | 556 | .set_timings = hdmi_display_set_timing, |
557 | .get_timings = hdmi_display_get_timings, | 557 | .get_timings = hdmi_display_get_timings, |
558 | 558 | ||
559 | .read_edid = hdmi_read_edid, | 559 | .read_edid = hdmi_read_edid, |
560 | .set_infoframe = hdmi_set_infoframe, | 560 | .set_infoframe = hdmi_set_infoframe, |
561 | .set_hdmi_mode = hdmi_set_hdmi_mode, | 561 | .set_hdmi_mode = hdmi_set_hdmi_mode, |
562 | }; | 562 | }; |
563 | 563 | ||
564 | static void hdmi_init_output(struct platform_device *pdev) | 564 | static void hdmi_init_output(struct platform_device *pdev) |
565 | { | 565 | { |
566 | struct omap_dss_device *out = &hdmi.output; | 566 | struct omap_dss_device *out = &hdmi.output; |
567 | 567 | ||
568 | out->dev = &pdev->dev; | 568 | out->dev = &pdev->dev; |
569 | out->id = OMAP_DSS_OUTPUT_HDMI; | 569 | out->id = OMAP_DSS_OUTPUT_HDMI; |
570 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; | 570 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; |
571 | out->name = "hdmi.0"; | 571 | out->name = "hdmi.0"; |
572 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | 572 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
573 | out->ops.hdmi = &hdmi_ops; | 573 | out->ops.hdmi = &hdmi_ops; |
574 | out->owner = THIS_MODULE; | 574 | out->owner = THIS_MODULE; |
575 | 575 | ||
576 | omapdss_register_output(out); | 576 | omapdss_register_output(out); |
577 | } | 577 | } |
578 | 578 | ||
579 | static void __exit hdmi_uninit_output(struct platform_device *pdev) | 579 | static void __exit hdmi_uninit_output(struct platform_device *pdev) |
580 | { | 580 | { |
581 | struct omap_dss_device *out = &hdmi.output; | 581 | struct omap_dss_device *out = &hdmi.output; |
582 | 582 | ||
583 | omapdss_unregister_output(out); | 583 | omapdss_unregister_output(out); |
584 | } | 584 | } |
585 | 585 | ||
586 | static int hdmi_probe_of(struct platform_device *pdev) | 586 | static int hdmi_probe_of(struct platform_device *pdev) |
587 | { | 587 | { |
588 | struct device_node *node = pdev->dev.of_node; | 588 | struct device_node *node = pdev->dev.of_node; |
589 | struct device_node *ep; | 589 | struct device_node *ep; |
590 | int r; | 590 | int r; |
591 | 591 | ||
592 | ep = omapdss_of_get_first_endpoint(node); | 592 | ep = omapdss_of_get_first_endpoint(node); |
593 | if (!ep) | 593 | if (!ep) |
594 | return 0; | 594 | return 0; |
595 | 595 | ||
596 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | 596 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); |
597 | if (r) | 597 | if (r) |
598 | goto err; | 598 | goto err; |
599 | 599 | ||
600 | of_node_put(ep); | 600 | of_node_put(ep); |
601 | return 0; | 601 | return 0; |
602 | 602 | ||
603 | err: | 603 | err: |
604 | of_node_put(ep); | 604 | of_node_put(ep); |
605 | return r; | 605 | return r; |
606 | } | 606 | } |
607 | 607 | ||
608 | #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | 608 | #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) |
609 | static int audio_startup(struct device *dev, | 609 | static int audio_startup(struct device *dev, |
610 | void (*abort_cb)(struct device *dev)) | 610 | void (*abort_cb)(struct device *dev)) |
611 | { | 611 | { |
612 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 612 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
613 | int ret = 0; | 613 | int ret = 0; |
614 | 614 | ||
615 | mutex_lock(&hd->lock); | 615 | mutex_lock(&hd->lock); |
616 | 616 | ||
617 | if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { | 617 | if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { |
618 | ret = -EPERM; | 618 | ret = -EPERM; |
619 | goto out; | 619 | goto out; |
620 | } | 620 | } |
621 | 621 | ||
622 | hd->audio_abort_cb = abort_cb; | 622 | hd->audio_abort_cb = abort_cb; |
623 | 623 | ||
624 | out: | 624 | out: |
625 | mutex_unlock(&hd->lock); | 625 | mutex_unlock(&hd->lock); |
626 | 626 | ||
627 | return ret; | 627 | return ret; |
628 | } | 628 | } |
629 | 629 | ||
630 | static int audio_enable(struct device *dev, bool enable) | 630 | static int audio_enable(struct device *dev, bool enable) |
631 | { | 631 | { |
632 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 632 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
633 | int ret; | 633 | int ret; |
634 | 634 | ||
635 | mutex_lock(&hd->lock); | 635 | mutex_lock(&hd->lock); |
636 | 636 | ||
637 | if (enable) { | 637 | if (enable) { |
638 | /* No idle while playing audio */ | 638 | /* No idle while playing audio */ |
639 | hd->wp_idlemode = | 639 | hd->wp_idlemode = |
640 | REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); | 640 | REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); |
641 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); | 641 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); |
642 | } | 642 | } |
643 | 643 | ||
644 | ret = hdmi_wp_audio_enable(&hd->wp, enable); | 644 | ret = hdmi_wp_audio_enable(&hd->wp, enable); |
645 | 645 | ||
646 | if (!enable) { | 646 | if (!enable) { |
647 | /* Playback stopped, restore original idlemode */ | 647 | /* Playback stopped, restore original idlemode */ |
648 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, | 648 | REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, |
649 | 3, 2); | 649 | 3, 2); |
650 | 650 | ||
651 | hd->audio_abort_cb = NULL; | 651 | hd->audio_abort_cb = NULL; |
652 | } | 652 | } |
653 | 653 | ||
654 | mutex_unlock(&hd->lock); | 654 | mutex_unlock(&hd->lock); |
655 | 655 | ||
656 | return ret; | 656 | return ret; |
657 | } | 657 | } |
658 | 658 | ||
659 | static int audio_start(struct device *dev, bool enable) | 659 | static int audio_start(struct device *dev, bool enable) |
660 | { | 660 | { |
661 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 661 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
662 | 662 | ||
663 | return hdmi_wp_audio_core_req_enable(&hd->wp, enable); | 663 | return hdmi_wp_audio_core_req_enable(&hd->wp, enable); |
664 | } | 664 | } |
665 | 665 | ||
666 | static int audio_config(struct device *dev, struct omap_dss_audio *dss_audio) | 666 | static int audio_config(struct device *dev, struct omap_dss_audio *dss_audio) |
667 | { | 667 | { |
668 | struct omap_hdmi *hd = dev_get_drvdata(dev); | 668 | struct omap_hdmi *hd = dev_get_drvdata(dev); |
669 | int ret; | 669 | int ret; |
670 | 670 | ||
671 | mutex_lock(&hd->lock); | 671 | mutex_lock(&hd->lock); |
672 | if (!hdmi_mode_has_audio(&hd->cfg)) | 672 | if (!hdmi_mode_has_audio(&hd->cfg)) |
673 | ret = -EPERM; | 673 | ret = -EPERM; |
674 | else | 674 | else |
675 | ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, | 675 | ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, |
676 | hd->cfg.timings.pixelclock); | 676 | hd->cfg.timings.pixelclock); |
677 | mutex_unlock(&hd->lock); | 677 | mutex_unlock(&hd->lock); |
678 | 678 | ||
679 | return ret; | 679 | return ret; |
680 | } | 680 | } |
681 | 681 | ||
682 | static int hdmi_audio_register(struct device *dev) | 682 | static int hdmi_audio_register(struct device *dev) |
683 | { | 683 | { |
684 | struct omap_hdmi_audio ha = { | 684 | struct omap_hdmi_audio ha = { |
685 | .dev = dev, | 685 | .dev = dev, |
686 | .hw_version = OMAP5_HDMI, | 686 | .hw_version = OMAP5_HDMI, |
687 | .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), | 687 | .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), |
688 | .audio_startup = audio_startup, | 688 | .audio_startup = audio_startup, |
689 | .audio_enable = audio_enable, | 689 | .audio_enable = audio_enable, |
690 | .audio_start = audio_start, | 690 | .audio_start = audio_start, |
691 | .audio_config = audio_config, | 691 | .audio_config = audio_config, |
692 | }; | 692 | }; |
693 | 693 | ||
694 | return omap_hdmi_audio_register(&ha); | 694 | return omap_hdmi_audio_register(&ha); |
695 | } | 695 | } |
696 | #else /* CONFIG_OMAP5_DSS_HDMI_AUDIO */ | 696 | #else /* CONFIG_OMAP5_DSS_HDMI_AUDIO */ |
697 | static int hdmi_audio_register(struct device *dev) | 697 | static int hdmi_audio_register(struct device *dev) |
698 | { | 698 | { |
699 | return 0; | 699 | return 0; |
700 | } | 700 | } |
701 | #endif | 701 | #endif |
702 | 702 | ||
703 | /* HDMI HW IP initialisation */ | 703 | /* HDMI HW IP initialisation */ |
704 | static int omapdss_hdmihw_probe(struct platform_device *pdev) | 704 | static int omapdss_hdmihw_probe(struct platform_device *pdev) |
705 | { | 705 | { |
706 | int r; | 706 | int r; |
707 | int irq; | 707 | int irq; |
708 | 708 | ||
709 | hdmi.pdev = pdev; | 709 | hdmi.pdev = pdev; |
710 | dev_set_drvdata(&pdev->dev, &hdmi); | 710 | dev_set_drvdata(&pdev->dev, &hdmi); |
711 | 711 | ||
712 | mutex_init(&hdmi.lock); | 712 | mutex_init(&hdmi.lock); |
713 | 713 | ||
714 | if (pdev->dev.of_node) { | 714 | if (pdev->dev.of_node) { |
715 | r = hdmi_probe_of(pdev); | 715 | r = hdmi_probe_of(pdev); |
716 | if (r) | 716 | if (r) |
717 | return r; | 717 | return r; |
718 | } | 718 | } |
719 | 719 | ||
720 | r = hdmi_wp_init(pdev, &hdmi.wp); | 720 | r = hdmi_wp_init(pdev, &hdmi.wp); |
721 | if (r) | 721 | if (r) |
722 | return r; | 722 | return r; |
723 | 723 | ||
724 | hdmi.pll = pll_create(pdev, "pll", "sys_clk", DSS_PLL_TYPE_HDMI, | 724 | hdmi.pll = pll_create(pdev, "pll", "sys_clk", DSS_PLL_TYPE_HDMI, |
725 | NULL, 0); | 725 | NULL, 0); |
726 | if (!hdmi.pll) | 726 | if (!hdmi.pll) |
727 | return -ENODEV; | 727 | return -ENODEV; |
728 | 728 | ||
729 | r = hdmi_phy_init(pdev, &hdmi.phy); | 729 | r = hdmi_phy_init(pdev, &hdmi.phy); |
730 | if (r) | 730 | if (r) |
731 | return r; | 731 | return r; |
732 | 732 | ||
733 | r = hdmi5_core_init(pdev, &hdmi.core); | 733 | r = hdmi5_core_init(pdev, &hdmi.core); |
734 | if (r) | 734 | if (r) |
735 | return r; | 735 | return r; |
736 | 736 | ||
737 | irq = platform_get_irq(pdev, 0); | 737 | irq = platform_get_irq(pdev, 0); |
738 | if (irq < 0) { | 738 | if (irq < 0) { |
739 | DSSERR("platform_get_irq failed\n"); | 739 | DSSERR("platform_get_irq failed\n"); |
740 | return -ENODEV; | 740 | return -ENODEV; |
741 | } | 741 | } |
742 | 742 | ||
743 | r = devm_request_threaded_irq(&pdev->dev, irq, | 743 | r = devm_request_threaded_irq(&pdev->dev, irq, |
744 | NULL, hdmi_irq_handler, | 744 | NULL, hdmi_irq_handler, |
745 | IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); | 745 | IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); |
746 | if (r) { | 746 | if (r) { |
747 | DSSERR("HDMI IRQ request failed\n"); | 747 | DSSERR("HDMI IRQ request failed\n"); |
748 | return r; | 748 | return r; |
749 | } | 749 | } |
750 | 750 | ||
751 | pm_runtime_enable(&pdev->dev); | 751 | pm_runtime_enable(&pdev->dev); |
752 | 752 | ||
753 | hdmi_init_output(pdev); | 753 | hdmi_init_output(pdev); |
754 | 754 | ||
755 | r = hdmi_audio_register(&pdev->dev); | 755 | r = hdmi_audio_register(&pdev->dev); |
756 | if (r) { | 756 | if (r) { |
757 | DSSERR("Registering HDMI audio failed\n"); | 757 | DSSERR("Registering HDMI audio failed\n"); |
758 | hdmi_uninit_output(pdev); | 758 | hdmi_uninit_output(pdev); |
759 | pm_runtime_disable(&pdev->dev); | 759 | pm_runtime_disable(&pdev->dev); |
760 | return r; | 760 | return r; |
761 | } | 761 | } |
762 | 762 | ||
763 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); | 763 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); |
764 | 764 | ||
765 | return 0; | 765 | return 0; |
766 | } | 766 | } |
767 | 767 | ||
768 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) | 768 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) |
769 | { | 769 | { |
770 | omap_hdmi_audio_unregister(&pdev->dev); | 770 | omap_hdmi_audio_unregister(&pdev->dev); |
771 | 771 | ||
772 | hdmi_uninit_output(pdev); | 772 | hdmi_uninit_output(pdev); |
773 | 773 | ||
774 | pm_runtime_disable(&pdev->dev); | 774 | pm_runtime_disable(&pdev->dev); |
775 | 775 | ||
776 | return 0; | 776 | return 0; |
777 | } | 777 | } |
778 | 778 | ||
779 | static int hdmi_runtime_suspend(struct device *dev) | 779 | static int hdmi_runtime_suspend(struct device *dev) |
780 | { | 780 | { |
781 | pll_enable_clock(hdmi.pll, false); | 781 | pll_enable_clock(hdmi.pll, false); |
782 | 782 | ||
783 | dispc_runtime_put(); | 783 | dispc_runtime_put(); |
784 | 784 | ||
785 | return 0; | 785 | return 0; |
786 | } | 786 | } |
787 | 787 | ||
788 | static int hdmi_runtime_resume(struct device *dev) | 788 | static int hdmi_runtime_resume(struct device *dev) |
789 | { | 789 | { |
790 | int r; | 790 | int r; |
791 | 791 | ||
792 | r = dispc_runtime_get(); | 792 | r = dispc_runtime_get(); |
793 | if (r < 0) | 793 | if (r < 0) |
794 | return r; | 794 | return r; |
795 | 795 | ||
796 | pll_enable_clock(hdmi.pll, true); | 796 | pll_enable_clock(hdmi.pll, true); |
797 | 797 | ||
798 | return 0; | 798 | return 0; |
799 | } | 799 | } |
800 | 800 | ||
801 | static const struct dev_pm_ops hdmi_pm_ops = { | 801 | static const struct dev_pm_ops hdmi_pm_ops = { |
802 | .runtime_suspend = hdmi_runtime_suspend, | 802 | .runtime_suspend = hdmi_runtime_suspend, |
803 | .runtime_resume = hdmi_runtime_resume, | 803 | .runtime_resume = hdmi_runtime_resume, |
804 | }; | 804 | }; |
805 | 805 | ||
806 | static const struct of_device_id hdmi_of_match[] = { | 806 | static const struct of_device_id hdmi_of_match[] = { |
807 | { .compatible = "ti,omap5-hdmi", }, | 807 | { .compatible = "ti,omap5-hdmi", }, |
808 | { .compatible = "ti,dra7-hdmi", }, | 808 | { .compatible = "ti,dra7-hdmi", }, |
809 | {}, | 809 | {}, |
810 | }; | 810 | }; |
811 | 811 | ||
812 | static struct platform_driver omapdss_hdmihw_driver = { | 812 | static struct platform_driver omapdss_hdmihw_driver = { |
813 | .probe = omapdss_hdmihw_probe, | 813 | .probe = omapdss_hdmihw_probe, |
814 | .remove = __exit_p(omapdss_hdmihw_remove), | 814 | .remove = __exit_p(omapdss_hdmihw_remove), |
815 | .driver = { | 815 | .driver = { |
816 | .name = "omapdss_hdmi5", | 816 | .name = "omapdss_hdmi5", |
817 | .owner = THIS_MODULE, | 817 | .owner = THIS_MODULE, |
818 | .pm = &hdmi_pm_ops, | 818 | .pm = &hdmi_pm_ops, |
819 | .of_match_table = hdmi_of_match, | 819 | .of_match_table = hdmi_of_match, |
820 | }, | 820 | }, |
821 | }; | 821 | }; |
822 | 822 | ||
823 | int __init hdmi5_init_platform_driver(void) | 823 | int __init hdmi5_init_platform_driver(void) |
824 | { | 824 | { |
825 | return platform_driver_register(&omapdss_hdmihw_driver); | 825 | return platform_driver_register(&omapdss_hdmihw_driver); |
826 | } | 826 | } |
827 | 827 | ||
828 | void __exit hdmi5_uninit_platform_driver(void) | 828 | void __exit hdmi5_uninit_platform_driver(void) |
829 | { | 829 | { |
830 | platform_driver_unregister(&omapdss_hdmihw_driver); | 830 | platform_driver_unregister(&omapdss_hdmihw_driver); |
831 | } | 831 | } |
832 | 832 |