blob: 1f2fbccaff1fa308d87f94c3403c1428ca44ee73 [file] [log] [blame]
Mythri P Kc3198a52011-03-12 12:04:27 +05301/*
Archit Tanejaef269582013-09-12 17:45:57 +05302 * HDMI interface DSS driver for TI's OMAP4 family of SoCs.
Mythri P Kc3198a52011-03-12 12:04:27 +05303 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Authors: Yong Zhi
5 * Mythri pk <mythripk@ti.com>
6 *
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
9 * the Free Software Foundation.
10 *
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
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
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/>.
18 */
19
20#define DSS_SUBSYS_NAME "HDMI"
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/err.h>
25#include <linux/io.h>
26#include <linux/interrupt.h>
27#include <linux/mutex.h>
28#include <linux/delay.h>
29#include <linux/string.h>
Tomi Valkeinen24e62892011-05-23 11:51:18 +030030#include <linux/platform_device.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030031#include <linux/pm_runtime.h>
32#include <linux/clk.h>
Tomi Valkeinencca35012012-04-26 14:48:32 +030033#include <linux/gpio.h>
Tomi Valkeinen17486942012-08-15 15:55:04 +030034#include <linux/regulator/consumer.h>
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030035#include <video/omapdss.h>
Mythri P Kc3198a52011-03-12 12:04:27 +053036
Archit Tanejaef269582013-09-12 17:45:57 +053037#include "hdmi4_core.h"
Mythri P Kc3198a52011-03-12 12:04:27 +053038#include "dss.h"
Ricardo Neriad44cc32011-05-18 22:31:56 -050039#include "dss_features.h"
Mythri P Kc3198a52011-03-12 12:04:27 +053040
41static struct {
42 struct mutex lock;
Mythri P Kc3198a52011-03-12 12:04:27 +053043 struct platform_device *pdev;
Ricardo Neri66a06b02012-11-06 00:19:14 -060044
Archit Taneja275cfa12013-10-08 14:22:03 +053045 struct hdmi_wp_data wp;
46 struct hdmi_pll_data pll;
47 struct hdmi_phy_data phy;
48 struct hdmi_core_data core;
49
50 struct hdmi_config cfg;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030051
52 struct clk *sys_clk;
Tomi Valkeinen17486942012-08-15 15:55:04 +030053 struct regulator *vdda_hdmi_dac_reg;
Tomi Valkeinencca35012012-04-26 14:48:32 +030054
Tomi Valkeinen0b450c32013-05-24 13:20:17 +030055 bool core_enabled;
56
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +030057 struct omap_dss_device output;
Mythri P Kc3198a52011-03-12 12:04:27 +053058} hdmi;
59
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030060static int hdmi_runtime_get(void)
61{
62 int r;
63
64 DSSDBG("hdmi_runtime_get\n");
65
66 r = pm_runtime_get_sync(&hdmi.pdev->dev);
67 WARN_ON(r < 0);
Archit Tanejaa247ce782012-02-10 11:45:52 +053068 if (r < 0)
Tomi Valkeinen852f0832012-02-17 17:58:04 +020069 return r;
Archit Tanejaa247ce782012-02-10 11:45:52 +053070
71 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030072}
73
74static void hdmi_runtime_put(void)
75{
76 int r;
77
78 DSSDBG("hdmi_runtime_put\n");
79
Tomi Valkeinen0eaf9f52012-01-23 13:23:08 +020080 r = pm_runtime_put_sync(&hdmi.pdev->dev);
Tomi Valkeinen5be3aeb2012-06-27 16:37:18 +030081 WARN_ON(r < 0 && r != -ENOSYS);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030082}
83
Tomi Valkeinendcf5f722013-10-28 11:47:34 +020084static irqreturn_t hdmi_irq_handler(int irq, void *data)
85{
86 struct hdmi_wp_data *wp = data;
87 u32 irqstatus;
88
89 irqstatus = hdmi_wp_get_irqstatus(wp);
90 hdmi_wp_set_irqstatus(wp, irqstatus);
91
92 if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
93 irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
94 /*
95 * If we get both connect and disconnect interrupts at the same
96 * time, turn off the PHY, clear interrupts, and restart, which
97 * raises connect interrupt if a cable is connected, or nothing
98 * if cable is not connected.
99 */
100 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
101
102 hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
103 HDMI_IRQ_LINK_DISCONNECT);
104
105 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
106 } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
107 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
108 } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
109 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
110 }
111
112 return IRQ_HANDLED;
113}
114
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300115static int hdmi_init_regulator(void)
116{
Tomi Valkeinen818a0532013-09-23 18:42:11 +0530117 int r;
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300118 struct regulator *reg;
119
120 if (hdmi.vdda_hdmi_dac_reg != NULL)
121 return 0;
122
Tomi Valkeinen931d4bd2013-06-10 14:05:10 +0300123 reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300124
125 if (IS_ERR(reg)) {
Tomi Valkeinen40359a92013-12-19 16:15:34 +0200126 if (PTR_ERR(reg) != -EPROBE_DEFER)
Tomi Valkeinen931d4bd2013-06-10 14:05:10 +0300127 DSSERR("can't get VDDA regulator\n");
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300128 return PTR_ERR(reg);
129 }
130
Tomi Valkeinen818a0532013-09-23 18:42:11 +0530131 if (regulator_can_change_voltage(reg)) {
132 r = regulator_set_voltage(reg, 1800000, 1800000);
133 if (r) {
134 devm_regulator_put(reg);
135 DSSWARN("can't set the regulator voltage\n");
136 return r;
137 }
138 }
139
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300140 hdmi.vdda_hdmi_dac_reg = reg;
141
142 return 0;
143}
144
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300145static int hdmi_power_on_core(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530146{
Mythri P K46095b22012-01-06 17:52:09 +0530147 int r;
Mythri P Kc3198a52011-03-12 12:04:27 +0530148
Tomi Valkeinen17486942012-08-15 15:55:04 +0300149 r = regulator_enable(hdmi.vdda_hdmi_dac_reg);
150 if (r)
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300151 return r;
Tomi Valkeinen17486942012-08-15 15:55:04 +0300152
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300153 r = hdmi_runtime_get();
154 if (r)
Tomi Valkeinencca35012012-04-26 14:48:32 +0300155 goto err_runtime_get;
Mythri P Kc3198a52011-03-12 12:04:27 +0530156
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300157 /* Make selection of HDMI in DSS */
158 dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
159
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300160 hdmi.core_enabled = true;
161
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300162 return 0;
163
164err_runtime_get:
165 regulator_disable(hdmi.vdda_hdmi_dac_reg);
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300166
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300167 return r;
168}
169
170static void hdmi_power_off_core(struct omap_dss_device *dssdev)
171{
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300172 hdmi.core_enabled = false;
173
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300174 hdmi_runtime_put();
175 regulator_disable(hdmi.vdda_hdmi_dac_reg);
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300176}
177
178static int hdmi_power_on_full(struct omap_dss_device *dssdev)
179{
180 int r;
181 struct omap_video_timings *p;
Tomi Valkeinen7ae9a712013-05-10 15:27:07 +0300182 struct omap_overlay_manager *mgr = hdmi.output.manager;
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200183 struct hdmi_wp_data *wp = &hdmi.wp;
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300184
185 r = hdmi_power_on_core(dssdev);
186 if (r)
187 return r;
188
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200189 /* disable and clear irqs */
190 hdmi_wp_clear_irqenable(wp, 0xffffffff);
191 hdmi_wp_set_irqstatus(wp, 0xffffffff);
192
Archit Taneja275cfa12013-10-08 14:22:03 +0530193 p = &hdmi.cfg.timings;
Mythri P Kc3198a52011-03-12 12:04:27 +0530194
Archit Taneja78493982012-08-08 16:50:42 +0530195 DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
Mythri P Kc3198a52011-03-12 12:04:27 +0530196
Tomi Valkeinen33f13122014-09-15 15:40:47 +0300197 hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), p->pixelclock);
Mythri P Kc3198a52011-03-12 12:04:27 +0530198
Mythri P K95a8aeb2011-09-08 19:06:18 +0530199 /* config the PLL and PHY hdmi_set_pll_pwrfirst */
Archit Taneja275cfa12013-10-08 14:22:03 +0530200 r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp);
Mythri P Kc3198a52011-03-12 12:04:27 +0530201 if (r) {
202 DSSDBG("Failed to lock PLL\n");
Tomi Valkeinencca35012012-04-26 14:48:32 +0300203 goto err_pll_enable;
Mythri P Kc3198a52011-03-12 12:04:27 +0530204 }
205
Tomi Valkeinen33f13122014-09-15 15:40:47 +0300206 r = hdmi_phy_configure(&hdmi.phy, hdmi.pll.info.clkdco,
207 hdmi.pll.info.clkout);
Mythri P Kc3198a52011-03-12 12:04:27 +0530208 if (r) {
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200209 DSSDBG("Failed to configure PHY\n");
210 goto err_phy_cfg;
Mythri P Kc3198a52011-03-12 12:04:27 +0530211 }
212
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200213 r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
214 if (r)
215 goto err_phy_pwr;
216
Archit Taneja275cfa12013-10-08 14:22:03 +0530217 hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
Mythri P Kc3198a52011-03-12 12:04:27 +0530218
Mythri P Kc3198a52011-03-12 12:04:27 +0530219 /* bypass TV gamma table */
220 dispc_enable_gamma_table(0);
221
222 /* tv size */
Archit Tanejacea87b92012-09-07 17:56:20 +0530223 dss_mgr_set_timings(mgr, p);
Mythri P Kc3198a52011-03-12 12:04:27 +0530224
Archit Taneja275cfa12013-10-08 14:22:03 +0530225 r = hdmi_wp_video_start(&hdmi.wp);
Ricardo Neric0456be2012-04-27 13:48:45 -0500226 if (r)
227 goto err_vid_enable;
Mythri P Kc3198a52011-03-12 12:04:27 +0530228
Archit Tanejacea87b92012-09-07 17:56:20 +0530229 r = dss_mgr_enable(mgr);
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200230 if (r)
231 goto err_mgr_enable;
Tomi Valkeinen3870c902011-08-31 14:47:11 +0300232
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200233 hdmi_wp_set_irqenable(wp,
234 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
235
Mythri P Kc3198a52011-03-12 12:04:27 +0530236 return 0;
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200237
238err_mgr_enable:
Archit Taneja275cfa12013-10-08 14:22:03 +0530239 hdmi_wp_video_stop(&hdmi.wp);
Ricardo Neric0456be2012-04-27 13:48:45 -0500240err_vid_enable:
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200241err_phy_cfg:
242 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
243err_phy_pwr:
Archit Taneja275cfa12013-10-08 14:22:03 +0530244 hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
Tomi Valkeinencca35012012-04-26 14:48:32 +0300245err_pll_enable:
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300246 hdmi_power_off_core(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530247 return -EIO;
248}
249
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300250static void hdmi_power_off_full(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530251{
Tomi Valkeinen7ae9a712013-05-10 15:27:07 +0300252 struct omap_overlay_manager *mgr = hdmi.output.manager;
Archit Tanejacea87b92012-09-07 17:56:20 +0530253
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200254 hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
255
Archit Tanejacea87b92012-09-07 17:56:20 +0530256 dss_mgr_disable(mgr);
Mythri P Kc3198a52011-03-12 12:04:27 +0530257
Archit Taneja275cfa12013-10-08 14:22:03 +0530258 hdmi_wp_video_stop(&hdmi.wp);
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200259
260 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
261
Archit Taneja275cfa12013-10-08 14:22:03 +0530262 hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
Tomi Valkeinencca35012012-04-26 14:48:32 +0300263
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300264 hdmi_power_off_core(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530265}
266
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300267static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
Mythri P Kc3198a52011-03-12 12:04:27 +0530268 struct omap_video_timings *timings)
269{
Archit Taneja1e676242013-12-09 19:39:08 +0530270 struct omap_dss_device *out = &hdmi.output;
Mythri P Kc3198a52011-03-12 12:04:27 +0530271
Archit Taneja1e676242013-12-09 19:39:08 +0530272 if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
Mythri P Kc3198a52011-03-12 12:04:27 +0530273 return -EINVAL;
Mythri P Kc3198a52011-03-12 12:04:27 +0530274
275 return 0;
Mythri P Kc3198a52011-03-12 12:04:27 +0530276}
277
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300278static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
Archit Taneja78493982012-08-08 16:50:42 +0530279 struct omap_video_timings *timings)
Mythri P Kc3198a52011-03-12 12:04:27 +0530280{
Archit Tanejaed1aa902012-08-15 00:40:31 +0530281 mutex_lock(&hdmi.lock);
282
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300283 hdmi.cfg.timings = *timings;
Archit Taneja78493982012-08-08 16:50:42 +0530284
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300285 dispc_set_tv_pclk(timings->pixelclock);
Archit Taneja1e676242013-12-09 19:39:08 +0530286
Archit Tanejaed1aa902012-08-15 00:40:31 +0530287 mutex_unlock(&hdmi.lock);
Mythri P Kc3198a52011-03-12 12:04:27 +0530288}
289
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300290static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300291 struct omap_video_timings *timings)
292{
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300293 *timings = hdmi.cfg.timings;
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300294}
295
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200296static void hdmi_dump_regs(struct seq_file *s)
Mythri P K162874d2011-09-22 13:37:45 +0530297{
298 mutex_lock(&hdmi.lock);
299
Wei Yongjunf8fb7d72012-10-21 20:54:26 +0800300 if (hdmi_runtime_get()) {
301 mutex_unlock(&hdmi.lock);
Mythri P K162874d2011-09-22 13:37:45 +0530302 return;
Wei Yongjunf8fb7d72012-10-21 20:54:26 +0800303 }
Mythri P K162874d2011-09-22 13:37:45 +0530304
Archit Taneja275cfa12013-10-08 14:22:03 +0530305 hdmi_wp_dump(&hdmi.wp, s);
306 hdmi_pll_dump(&hdmi.pll, s);
307 hdmi_phy_dump(&hdmi.phy, s);
308 hdmi4_core_dump(&hdmi.core, s);
Mythri P K162874d2011-09-22 13:37:45 +0530309
310 hdmi_runtime_put();
311 mutex_unlock(&hdmi.lock);
312}
313
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300314static int read_edid(u8 *buf, int len)
Tomi Valkeinen47024562011-08-25 17:12:56 +0300315{
316 int r;
317
318 mutex_lock(&hdmi.lock);
319
320 r = hdmi_runtime_get();
321 BUG_ON(r);
322
Archit Taneja275cfa12013-10-08 14:22:03 +0530323 r = hdmi4_read_edid(&hdmi.core, buf, len);
Tomi Valkeinen47024562011-08-25 17:12:56 +0300324
325 hdmi_runtime_put();
326 mutex_unlock(&hdmi.lock);
327
328 return r;
329}
330
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300331static int hdmi_display_enable(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530332{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300333 struct omap_dss_device *out = &hdmi.output;
Mythri P Kc3198a52011-03-12 12:04:27 +0530334 int r = 0;
335
336 DSSDBG("ENTER hdmi_display_enable\n");
337
338 mutex_lock(&hdmi.lock);
339
Archit Tanejacea87b92012-09-07 17:56:20 +0530340 if (out == NULL || out->manager == NULL) {
341 DSSERR("failed to enable display: no output/manager\n");
Tomi Valkeinen05e1d602011-06-23 16:38:21 +0300342 r = -ENODEV;
343 goto err0;
344 }
345
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300346 r = hdmi_power_on_full(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530347 if (r) {
348 DSSERR("failed to power on device\n");
Tomi Valkeinend3923932013-04-25 13:12:07 +0300349 goto err0;
Mythri P Kc3198a52011-03-12 12:04:27 +0530350 }
351
352 mutex_unlock(&hdmi.lock);
353 return 0;
354
Mythri P Kc3198a52011-03-12 12:04:27 +0530355err0:
356 mutex_unlock(&hdmi.lock);
357 return r;
358}
359
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300360static void hdmi_display_disable(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530361{
362 DSSDBG("Enter hdmi_display_disable\n");
363
364 mutex_lock(&hdmi.lock);
365
Tomi Valkeinenbb426fc92012-10-19 17:42:10 +0300366 hdmi_power_off_full(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530367
Mythri P Kc3198a52011-03-12 12:04:27 +0530368 mutex_unlock(&hdmi.lock);
369}
370
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300371static int hdmi_core_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen44898232012-10-19 17:42:27 +0300372{
373 int r = 0;
374
375 DSSDBG("ENTER omapdss_hdmi_core_enable\n");
376
377 mutex_lock(&hdmi.lock);
378
Tomi Valkeinen44898232012-10-19 17:42:27 +0300379 r = hdmi_power_on_core(dssdev);
380 if (r) {
381 DSSERR("failed to power on device\n");
382 goto err0;
383 }
384
385 mutex_unlock(&hdmi.lock);
386 return 0;
387
388err0:
389 mutex_unlock(&hdmi.lock);
390 return r;
391}
392
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300393static void hdmi_core_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen44898232012-10-19 17:42:27 +0300394{
395 DSSDBG("Enter omapdss_hdmi_core_disable\n");
396
397 mutex_lock(&hdmi.lock);
398
399 hdmi_power_off_core(dssdev);
400
401 mutex_unlock(&hdmi.lock);
402}
403
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300404static int hdmi_get_clocks(struct platform_device *pdev)
405{
406 struct clk *clk;
407
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300408 clk = devm_clk_get(&pdev->dev, "sys_clk");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300409 if (IS_ERR(clk)) {
410 DSSERR("can't get sys_clk\n");
411 return PTR_ERR(clk);
412 }
413
414 hdmi.sys_clk = clk;
415
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300416 return 0;
417}
418
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300419static int hdmi_connect(struct omap_dss_device *dssdev,
420 struct omap_dss_device *dst)
421{
422 struct omap_overlay_manager *mgr;
423 int r;
424
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300425 r = hdmi_init_regulator();
426 if (r)
427 return r;
428
429 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
430 if (!mgr)
431 return -ENODEV;
432
433 r = dss_mgr_connect(mgr, dssdev);
434 if (r)
435 return r;
436
437 r = omapdss_output_set_device(dssdev, dst);
438 if (r) {
439 DSSERR("failed to connect output to new device: %s\n",
440 dst->name);
441 dss_mgr_disconnect(mgr, dssdev);
442 return r;
443 }
444
445 return 0;
446}
447
448static void hdmi_disconnect(struct omap_dss_device *dssdev,
449 struct omap_dss_device *dst)
450{
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300451 WARN_ON(dst != dssdev->dst);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300452
Tomi Valkeinen9560dc102013-07-24 13:06:54 +0300453 if (dst != dssdev->dst)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300454 return;
455
456 omapdss_output_unset_device(dssdev);
457
458 if (dssdev->manager)
459 dss_mgr_disconnect(dssdev->manager, dssdev);
460}
461
462static int hdmi_read_edid(struct omap_dss_device *dssdev,
463 u8 *edid, int len)
464{
465 bool need_enable;
466 int r;
467
468 need_enable = hdmi.core_enabled == false;
469
470 if (need_enable) {
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300471 r = hdmi_core_enable(dssdev);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300472 if (r)
473 return r;
474 }
475
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300476 r = read_edid(edid, len);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300477
478 if (need_enable)
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300479 hdmi_core_disable(dssdev);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300480
481 return r;
482}
483
484#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300485static int hdmi_audio_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300486{
487 int r;
488
489 mutex_lock(&hdmi.lock);
490
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300491 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) {
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300492 r = -EPERM;
493 goto err;
494 }
495
Archit Taneja275cfa12013-10-08 14:22:03 +0530496 r = hdmi_wp_audio_enable(&hdmi.wp, true);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300497 if (r)
498 goto err;
499
500 mutex_unlock(&hdmi.lock);
501 return 0;
502
503err:
504 mutex_unlock(&hdmi.lock);
505 return r;
506}
507
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300508static void hdmi_audio_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300509{
Archit Taneja275cfa12013-10-08 14:22:03 +0530510 hdmi_wp_audio_enable(&hdmi.wp, false);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300511}
512
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300513static int hdmi_audio_start(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300514{
Archit Taneja275cfa12013-10-08 14:22:03 +0530515 return hdmi4_audio_start(&hdmi.core, &hdmi.wp);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300516}
517
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300518static void hdmi_audio_stop(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300519{
Archit Taneja275cfa12013-10-08 14:22:03 +0530520 hdmi4_audio_stop(&hdmi.core, &hdmi.wp);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300521}
522
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300523static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300524{
525 bool r;
526
527 mutex_lock(&hdmi.lock);
528
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300529 r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300530
531 mutex_unlock(&hdmi.lock);
532 return r;
533}
534
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300535static int hdmi_audio_config(struct omap_dss_device *dssdev,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300536 struct omap_dss_audio *audio)
537{
538 int r;
Tomi Valkeinend8d789412013-04-10 14:12:14 +0300539 u32 pclk = hdmi.cfg.timings.pixelclock;
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300540
541 mutex_lock(&hdmi.lock);
542
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300543 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) {
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300544 r = -EPERM;
545 goto err;
546 }
547
Archit Taneja08d83e4e2013-09-17 11:43:15 +0530548 r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300549 if (r)
550 goto err;
551
552 mutex_unlock(&hdmi.lock);
553 return 0;
554
555err:
556 mutex_unlock(&hdmi.lock);
557 return r;
558}
559#else
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300560static int hdmi_audio_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300561{
562 return -EPERM;
563}
564
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300565static void hdmi_audio_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300566{
567}
568
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300569static int hdmi_audio_start(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300570{
571 return -EPERM;
572}
573
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300574static void hdmi_audio_stop(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300575{
576}
577
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300578static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300579{
580 return false;
581}
582
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300583static int hdmi_audio_config(struct omap_dss_device *dssdev,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300584 struct omap_dss_audio *audio)
585{
586 return -EPERM;
587}
588#endif
589
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300590static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
591 const struct hdmi_avi_infoframe *avi)
592{
593 hdmi.cfg.infoframe = *avi;
594 return 0;
595}
596
597static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
598 bool hdmi_mode)
599{
600 hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
601 return 0;
602}
603
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300604static const struct omapdss_hdmi_ops hdmi_ops = {
605 .connect = hdmi_connect,
606 .disconnect = hdmi_disconnect,
607
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300608 .enable = hdmi_display_enable,
609 .disable = hdmi_display_disable,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300610
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300611 .check_timings = hdmi_display_check_timing,
612 .set_timings = hdmi_display_set_timing,
613 .get_timings = hdmi_display_get_timings,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300614
615 .read_edid = hdmi_read_edid,
Tomi Valkeinenab0aee92014-06-18 14:21:44 +0300616 .set_infoframe = hdmi_set_infoframe,
617 .set_hdmi_mode = hdmi_set_hdmi_mode,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300618
Tomi Valkeinen164ebdd2013-05-15 10:48:45 +0300619 .audio_enable = hdmi_audio_enable,
620 .audio_disable = hdmi_audio_disable,
621 .audio_start = hdmi_audio_start,
622 .audio_stop = hdmi_audio_stop,
623 .audio_supported = hdmi_audio_supported,
624 .audio_config = hdmi_audio_config,
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300625};
626
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300627static void hdmi_init_output(struct platform_device *pdev)
Archit Taneja81b87f52012-09-26 16:30:49 +0530628{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300629 struct omap_dss_device *out = &hdmi.output;
Archit Taneja81b87f52012-09-26 16:30:49 +0530630
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300631 out->dev = &pdev->dev;
Archit Taneja81b87f52012-09-26 16:30:49 +0530632 out->id = OMAP_DSS_OUTPUT_HDMI;
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300633 out->output_type = OMAP_DISPLAY_TYPE_HDMI;
Tomi Valkeinen7286a082013-02-18 13:06:01 +0200634 out->name = "hdmi.0";
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +0200635 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300636 out->ops.hdmi = &hdmi_ops;
Tomi Valkeinenb7328e12013-05-03 11:42:18 +0300637 out->owner = THIS_MODULE;
Archit Taneja81b87f52012-09-26 16:30:49 +0530638
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +0300639 omapdss_register_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +0530640}
641
642static void __exit hdmi_uninit_output(struct platform_device *pdev)
643{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300644 struct omap_dss_device *out = &hdmi.output;
Archit Taneja81b87f52012-09-26 16:30:49 +0530645
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +0300646 omapdss_unregister_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +0530647}
648
Tomi Valkeinen2f5dc672014-04-17 12:54:02 +0300649static int hdmi_probe_of(struct platform_device *pdev)
650{
651 struct device_node *node = pdev->dev.of_node;
652 struct device_node *ep;
653 int r;
654
655 ep = omapdss_of_get_first_endpoint(node);
656 if (!ep)
657 return 0;
658
659 r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
660 if (r)
661 goto err;
662
663 of_node_put(ep);
664 return 0;
665
666err:
667 of_node_put(ep);
668 return r;
669}
670
Mythri P Kc3198a52011-03-12 12:04:27 +0530671/* HDMI HW IP initialisation */
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300672static int omapdss_hdmihw_probe(struct platform_device *pdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530673{
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300674 int r;
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200675 int irq;
Mythri P Kc3198a52011-03-12 12:04:27 +0530676
Mythri P Kc3198a52011-03-12 12:04:27 +0530677 hdmi.pdev = pdev;
678
679 mutex_init(&hdmi.lock);
680
Tomi Valkeinen2f5dc672014-04-17 12:54:02 +0300681 if (pdev->dev.of_node) {
682 r = hdmi_probe_of(pdev);
683 if (r)
684 return r;
685 }
686
Archit Taneja275cfa12013-10-08 14:22:03 +0530687 r = hdmi_wp_init(pdev, &hdmi.wp);
Archit Tanejaf382d9e2013-08-06 14:56:55 +0530688 if (r)
689 return r;
Mythri P Kc3198a52011-03-12 12:04:27 +0530690
Archit Taneja275cfa12013-10-08 14:22:03 +0530691 r = hdmi_pll_init(pdev, &hdmi.pll);
Archit Tanejac1577c12013-10-08 12:55:26 +0530692 if (r)
693 return r;
694
Archit Taneja275cfa12013-10-08 14:22:03 +0530695 r = hdmi_phy_init(pdev, &hdmi.phy);
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530696 if (r)
697 return r;
Tomi Valkeinenddb1d5c2013-06-06 13:08:35 +0300698
Archit Taneja275cfa12013-10-08 14:22:03 +0530699 r = hdmi4_core_init(pdev, &hdmi.core);
Archit Taneja425f02f2013-10-08 14:16:05 +0530700 if (r)
701 return r;
702
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300703 r = hdmi_get_clocks(pdev);
704 if (r) {
Ricardo Neri47e443b2012-11-06 00:19:12 -0600705 DSSERR("can't get clocks\n");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300706 return r;
707 }
708
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200709 irq = platform_get_irq(pdev, 0);
710 if (irq < 0) {
711 DSSERR("platform_get_irq failed\n");
712 return -ENODEV;
713 }
714
715 r = devm_request_threaded_irq(&pdev->dev, irq,
716 NULL, hdmi_irq_handler,
717 IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
718 if (r) {
719 DSSERR("HDMI IRQ request failed\n");
720 return r;
721 }
722
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300723 pm_runtime_enable(&pdev->dev);
724
Tomi Valkeinen002d3682013-02-13 12:17:43 +0200725 hdmi_init_output(pdev);
726
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200727 dss_debugfs_create_file("hdmi", hdmi_dump_regs);
728
Tomi Valkeinencca35012012-04-26 14:48:32 +0300729 return 0;
730}
731
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +0200732static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530733{
Archit Taneja81b87f52012-09-26 16:30:49 +0530734 hdmi_uninit_output(pdev);
735
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300736 pm_runtime_disable(&pdev->dev);
737
Mythri P Kc3198a52011-03-12 12:04:27 +0530738 return 0;
739}
740
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300741static int hdmi_runtime_suspend(struct device *dev)
742{
Rajendra Nayakf11766d2012-06-27 14:21:26 +0530743 clk_disable_unprepare(hdmi.sys_clk);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300744
745 dispc_runtime_put();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300746
747 return 0;
748}
749
750static int hdmi_runtime_resume(struct device *dev)
751{
752 int r;
753
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300754 r = dispc_runtime_get();
755 if (r < 0)
Tomi Valkeinen852f0832012-02-17 17:58:04 +0200756 return r;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300757
Rajendra Nayakf11766d2012-06-27 14:21:26 +0530758 clk_prepare_enable(hdmi.sys_clk);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300759
760 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300761}
762
763static const struct dev_pm_ops hdmi_pm_ops = {
764 .runtime_suspend = hdmi_runtime_suspend,
765 .runtime_resume = hdmi_runtime_resume,
766};
767
Tomi Valkeinen04656162013-12-16 15:14:04 +0200768static const struct of_device_id hdmi_of_match[] = {
769 { .compatible = "ti,omap4-hdmi", },
770 {},
771};
772
Mythri P Kc3198a52011-03-12 12:04:27 +0530773static struct platform_driver omapdss_hdmihw_driver = {
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300774 .probe = omapdss_hdmihw_probe,
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +0200775 .remove = __exit_p(omapdss_hdmihw_remove),
Mythri P Kc3198a52011-03-12 12:04:27 +0530776 .driver = {
777 .name = "omapdss_hdmi",
778 .owner = THIS_MODULE,
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300779 .pm = &hdmi_pm_ops,
Tomi Valkeinen04656162013-12-16 15:14:04 +0200780 .of_match_table = hdmi_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +0300781 .suppress_bind_attrs = true,
Mythri P Kc3198a52011-03-12 12:04:27 +0530782 },
783};
784
Archit Tanejaef269582013-09-12 17:45:57 +0530785int __init hdmi4_init_platform_driver(void)
Mythri P Kc3198a52011-03-12 12:04:27 +0530786{
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300787 return platform_driver_register(&omapdss_hdmihw_driver);
Mythri P Kc3198a52011-03-12 12:04:27 +0530788}
789
Archit Tanejaef269582013-09-12 17:45:57 +0530790void __exit hdmi4_uninit_platform_driver(void)
Mythri P Kc3198a52011-03-12 12:04:27 +0530791{
Tomi Valkeinen04c742c2012-02-23 15:32:37 +0200792 platform_driver_unregister(&omapdss_hdmihw_driver);
Mythri P Kc3198a52011-03-12 12:04:27 +0530793}