blob: 52438404c8c9720722bd92fcafc122e74d369bd9 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090010 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
Seung-Woo Kimd8408322011-12-21 17:39:39 +090013 * option) any later version.
14 *
15 */
16
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
18#include <drm/drm_edid.h>
19#include <drm/drm_crtc_helper.h>
Gustavo Padovan4ea95262015-06-01 12:04:44 -030020#include <drm/drm_atomic_helper.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090021
22#include "regs-hdmi.h"
23
24#include <linux/kernel.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090025#include <linux/wait.h>
26#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090027#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
Andrzej Hajda2228b7c2015-09-25 14:48:24 +020033#include <linux/gpio/consumer.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090034#include <linux/regulator/consumer.h>
Rahul Sharma22c4f422012-10-04 20:48:55 +053035#include <linux/io.h>
Rahul Sharmad5e9ca42014-05-09 15:34:18 +090036#include <linux/of_address.h>
Andrzej Hajdacd240cd2015-07-09 16:28:09 +020037#include <linux/of_device.h>
Andrzej Hajdaaa181572017-02-01 09:29:14 +010038#include <linux/of_graph.h>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053039#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090040#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053041#include <linux/mfd/syscon.h>
42#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090043
44#include <drm/exynos_drm.h>
45
Inki Daef37cd5e2014-05-09 14:25:20 +090046#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090047
Sean Paul724fd142014-05-09 15:05:10 +090048#define HOTPLUG_DEBOUNCE_MS 1100
49
Rahul Sharma5a325072012-10-04 20:48:54 +053050enum hdmi_type {
51 HDMI_TYPE13,
52 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020053 HDMI_TYPE_COUNT
54};
55
56#define HDMI_MAPPED_BASE 0xffff0000
57
58enum hdmi_mapped_regs {
59 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
60 HDMI_PHY_RSTOUT,
61 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020062 HDMI_ACR_MCTS0,
63 HDMI_ACR_CTS0,
64 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020065};
66
67static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
68 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
69 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
70 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020071 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
72 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
73 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053074};
75
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020076static const char * const supply[] = {
77 "vdd",
78 "vdd_osc",
79 "vdd_pll",
80};
81
Andrzej Hajda65e98032015-11-02 14:16:41 +010082struct hdmiphy_config {
83 int pixel_clock;
84 u8 conf[32];
85};
86
87struct hdmiphy_configs {
88 int count;
89 const struct hdmiphy_config *data;
90};
91
Andrzej Hajda9be7e982016-01-14 14:22:47 +090092struct string_array_spec {
93 int count;
94 const char * const *data;
95};
96
97#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
98
Inki Daebfe4e842014-03-06 14:18:17 +090099struct hdmi_driver_data {
100 unsigned int type;
101 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900102 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100103 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900104 struct string_array_spec clk_gates;
105 /*
106 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
107 * required parents of clock when HDMI-PHY is respectively off or on.
108 */
109 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900110};
111
Joonyoung Shim590f4182012-03-16 18:47:14 +0900112struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300113 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900114 struct device *dev;
115 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500116 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900117 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900118 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900119 struct delayed_work hotplug_work;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530120 struct drm_display_mode current_mode;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200121 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900122
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200123 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900124 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200125 struct i2c_client *hdmiphy_port;
126 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900127 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200128 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530129 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900130 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900131 struct clk **clk_gates;
132 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200133 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
134 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900135 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100136 struct drm_bridge *bridge;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900137};
138
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300139static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100140{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900141 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100142}
143
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200144static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
145{
146 return container_of(c, struct hdmi_context, connector);
147}
148
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900149static const struct hdmiphy_config hdmiphy_v13_configs[] = {
150 {
151 .pixel_clock = 27000000,
152 .conf = {
153 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
154 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
155 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200156 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900157 },
158 },
159 {
160 .pixel_clock = 27027000,
161 .conf = {
162 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
163 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
164 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200165 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900166 },
167 },
168 {
169 .pixel_clock = 74176000,
170 .conf = {
171 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
172 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
173 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200174 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900175 },
176 },
177 {
178 .pixel_clock = 74250000,
179 .conf = {
180 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
181 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
182 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200183 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900184 },
185 },
186 {
187 .pixel_clock = 148500000,
188 .conf = {
189 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
190 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
191 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200192 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900193 },
194 },
195};
196
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500197static const struct hdmiphy_config hdmiphy_v14_configs[] = {
198 {
199 .pixel_clock = 25200000,
200 .conf = {
201 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
202 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
203 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
204 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
205 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900206 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500207 {
208 .pixel_clock = 27000000,
209 .conf = {
210 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
211 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
212 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
213 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
214 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900215 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500216 {
217 .pixel_clock = 27027000,
218 .conf = {
219 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
220 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
221 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200222 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500223 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900224 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500225 {
226 .pixel_clock = 36000000,
227 .conf = {
228 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
229 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
230 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
231 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
232 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900233 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500234 {
235 .pixel_clock = 40000000,
236 .conf = {
237 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
238 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
239 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
240 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
241 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900242 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500243 {
244 .pixel_clock = 65000000,
245 .conf = {
246 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
247 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
248 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
249 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
250 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900251 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500252 {
Shirish Se1d883c2014-03-13 14:28:27 +0900253 .pixel_clock = 71000000,
254 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530255 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
256 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
257 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900258 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
259 },
260 },
261 {
262 .pixel_clock = 73250000,
263 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530264 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
265 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
266 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900267 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
268 },
269 },
270 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500271 .pixel_clock = 74176000,
272 .conf = {
273 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
274 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
275 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
276 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
277 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900278 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500279 {
280 .pixel_clock = 74250000,
281 .conf = {
282 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
283 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
284 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200285 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500286 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900287 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500288 {
289 .pixel_clock = 83500000,
290 .conf = {
291 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
292 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
293 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
294 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
295 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900296 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500297 {
298 .pixel_clock = 106500000,
299 .conf = {
300 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
301 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
302 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
303 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
304 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900305 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500306 {
307 .pixel_clock = 108000000,
308 .conf = {
309 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
310 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
311 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
312 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
313 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900314 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500315 {
Shirish Se1d883c2014-03-13 14:28:27 +0900316 .pixel_clock = 115500000,
317 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530318 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
319 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
320 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900321 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
322 },
323 },
324 {
325 .pixel_clock = 119000000,
326 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530327 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
328 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
329 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900330 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
331 },
332 },
333 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500334 .pixel_clock = 146250000,
335 .conf = {
336 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
337 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
338 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
339 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
340 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900341 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500342 {
343 .pixel_clock = 148500000,
344 .conf = {
345 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
346 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
347 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200348 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500349 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900350 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900351};
352
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530353static const struct hdmiphy_config hdmiphy_5420_configs[] = {
354 {
355 .pixel_clock = 25200000,
356 .conf = {
357 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
358 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
359 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
360 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
361 },
362 },
363 {
364 .pixel_clock = 27000000,
365 .conf = {
366 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
367 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
368 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
369 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
370 },
371 },
372 {
373 .pixel_clock = 27027000,
374 .conf = {
375 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
376 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
377 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
378 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
379 },
380 },
381 {
382 .pixel_clock = 36000000,
383 .conf = {
384 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
385 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
386 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
387 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
388 },
389 },
390 {
391 .pixel_clock = 40000000,
392 .conf = {
393 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
394 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
395 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
396 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
397 },
398 },
399 {
400 .pixel_clock = 65000000,
401 .conf = {
402 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
403 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
404 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
405 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
406 },
407 },
408 {
409 .pixel_clock = 71000000,
410 .conf = {
411 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
412 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
413 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
414 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
415 },
416 },
417 {
418 .pixel_clock = 73250000,
419 .conf = {
420 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
421 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
422 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
423 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
424 },
425 },
426 {
427 .pixel_clock = 74176000,
428 .conf = {
429 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
430 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
431 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
432 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
433 },
434 },
435 {
436 .pixel_clock = 74250000,
437 .conf = {
438 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
439 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
440 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
441 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
442 },
443 },
444 {
445 .pixel_clock = 83500000,
446 .conf = {
447 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
448 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
449 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
450 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
451 },
452 },
453 {
454 .pixel_clock = 88750000,
455 .conf = {
456 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
457 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
458 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
459 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
460 },
461 },
462 {
463 .pixel_clock = 106500000,
464 .conf = {
465 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
466 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
467 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
468 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
469 },
470 },
471 {
472 .pixel_clock = 108000000,
473 .conf = {
474 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
475 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
476 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
477 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
478 },
479 },
480 {
481 .pixel_clock = 115500000,
482 .conf = {
483 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
484 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
485 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
486 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
487 },
488 },
489 {
490 .pixel_clock = 146250000,
491 .conf = {
492 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
493 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
494 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
495 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
496 },
497 },
498 {
499 .pixel_clock = 148500000,
500 .conf = {
501 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
502 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
503 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
504 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
505 },
506 },
507};
508
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900509static const struct hdmiphy_config hdmiphy_5433_configs[] = {
510 {
511 .pixel_clock = 27000000,
512 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100513 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
514 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
515 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900516 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
517 },
518 },
519 {
520 .pixel_clock = 27027000,
521 .conf = {
522 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100523 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
524 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
525 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900526 },
527 },
528 {
529 .pixel_clock = 40000000,
530 .conf = {
531 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
532 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
533 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
534 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
535 },
536 },
537 {
538 .pixel_clock = 50000000,
539 .conf = {
540 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
541 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
542 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
543 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
544 },
545 },
546 {
547 .pixel_clock = 65000000,
548 .conf = {
549 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
550 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
551 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
552 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
553 },
554 },
555 {
556 .pixel_clock = 74176000,
557 .conf = {
558 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
559 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
560 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
561 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
562 },
563 },
564 {
565 .pixel_clock = 74250000,
566 .conf = {
567 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
568 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
569 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
570 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
571 },
572 },
573 {
574 .pixel_clock = 108000000,
575 .conf = {
576 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
577 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
578 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
579 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
580 },
581 },
582 {
583 .pixel_clock = 148500000,
584 .conf = {
585 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
586 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
587 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
588 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
589 },
590 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100591 {
592 .pixel_clock = 297000000,
593 .conf = {
594 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
595 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
596 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
597 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
598 },
599 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900600};
601
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100602static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900603 "hdmi", "sclk_hdmi"
604};
605
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100606static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900607 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
608};
609
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900610static const char * const hdmi_clk_gates5433[] = {
611 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
612};
613
614static const char * const hdmi_clk_muxes5433[] = {
615 "oscclk", "tmds_clko", "tmds_clko_user",
616 "oscclk", "pixel_clko", "pixel_clko_user"
617};
618
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900619static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
620 .type = HDMI_TYPE13,
621 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900622 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
623 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530624};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900625
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100626static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900627 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100628 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900629 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
630 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900631};
632
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900633static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
634 .type = HDMI_TYPE14,
635 .is_apb_phy = 1,
636 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900637 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
638 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200639};
640
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900641static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
642 .type = HDMI_TYPE14,
643 .is_apb_phy = 1,
644 .has_sysreg = 1,
645 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
646 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
647 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
648};
649
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200650static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
651{
652 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
653 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
654 return reg_id;
655}
656
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900657static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
658{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200659 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900660}
661
662static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
663 u32 reg_id, u8 value)
664{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200665 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900666}
667
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200668static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
669 int bytes, u32 val)
670{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200671 reg_id = hdmi_map_reg(hdata, reg_id);
672
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200673 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200674 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200675 val >>= 8;
676 reg_id += 4;
677 }
678}
679
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100680static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
681 u8 *buf, int size)
682{
683 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
684 writel(*buf++, hdata->regs + reg_id);
685}
686
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900687static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
688 u32 reg_id, u32 value, u32 mask)
689{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200690 u32 old;
691
692 reg_id = hdmi_map_reg(hdata, reg_id);
693 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900694 value = (value & mask) | (old & ~mask);
695 writel(value, hdata->regs + reg_id);
696}
697
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900698static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
699 u32 reg_offset, const u8 *buf, u32 len)
700{
701 if ((reg_offset + len) > 32)
702 return -EINVAL;
703
704 if (hdata->hdmiphy_port) {
705 int ret;
706
707 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
708 if (ret == len)
709 return 0;
710 return ret;
711 } else {
712 int i;
713 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200714 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900715 ((reg_offset + i)<<2));
716 return 0;
717 }
718}
719
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900720static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
721{
722 int i, ret;
723
724 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
725 ret = clk_prepare_enable(hdata->clk_gates[i]);
726 if (!ret)
727 continue;
728
729 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
730 hdata->drv_data->clk_gates.data[i], ret);
731 while (i--)
732 clk_disable_unprepare(hdata->clk_gates[i]);
733 return ret;
734 }
735
736 return 0;
737}
738
739static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
740{
741 int i = hdata->drv_data->clk_gates.count;
742
743 while (i--)
744 clk_disable_unprepare(hdata->clk_gates[i]);
745}
746
747static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
748{
749 struct device *dev = hdata->dev;
750 int ret = 0;
751 int i;
752
753 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
754 struct clk **c = &hdata->clk_muxes[i];
755
756 ret = clk_set_parent(c[2], c[to_phy]);
757 if (!ret)
758 continue;
759
760 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
761 hdata->drv_data->clk_muxes.data[i + 2],
762 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
763 }
764
765 return ret;
766}
767
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100768static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530769{
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100770 union hdmi_infoframe frm;
771 u8 buf[25];
772 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530773
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530774 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530775 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
776 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100777 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
778 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530779 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
780 return;
781 }
782
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100783 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
784 &hdata->current_mode);
785 if (!ret)
786 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
787 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530788 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100789 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
790 } else {
791 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
792 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530793
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100794 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
795 &hdata->current_mode);
796 if (!ret)
797 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
798 sizeof(buf));
799 if (ret > 0) {
800 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100801 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
802 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100803 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530804
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100805 ret = hdmi_audio_infoframe_init(&frm.audio);
806 if (!ret) {
807 frm.audio.channels = 2;
808 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
809 }
810 if (ret > 0) {
811 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
812 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530813 }
814}
815
Sean Pauld9716ee2014-01-30 16:19:29 -0500816static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
817 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500818{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200819 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500820
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200821 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200822 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530823
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200824 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500825}
826
Sean Pauld9716ee2014-01-30 16:19:29 -0500827static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900828{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200829 drm_connector_unregister(connector);
830 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900831}
832
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100833static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300834 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500835 .fill_modes = drm_helper_probe_single_connector_modes,
836 .detect = hdmi_detect,
837 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300838 .reset = drm_atomic_helper_connector_reset,
839 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
840 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500841};
842
843static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900844{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200845 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500846 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200847 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900848
Inki Dae8fa04aa2014-03-13 16:38:31 +0900849 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500850 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900851
Inki Dae8fa04aa2014-03-13 16:38:31 +0900852 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500853 if (!edid)
854 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900855
Sean Pauld9716ee2014-01-30 16:19:29 -0500856 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500857 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
858 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500859 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500860
Sean Pauld9716ee2014-01-30 16:19:29 -0500861 drm_mode_connector_update_edid_property(connector, edid);
862
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200863 ret = drm_add_edid_modes(connector, edid);
864
865 kfree(edid);
866
867 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900868}
869
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900870static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900871{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100872 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900873 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900874
Andrzej Hajda65e98032015-11-02 14:16:41 +0100875 for (i = 0; i < confs->count; i++)
876 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500877 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500878
879 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
880 return -EINVAL;
881}
882
Sean Pauld9716ee2014-01-30 16:19:29 -0500883static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500884 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900885{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200886 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900887 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900888
Rahul Sharma16844fb2013-06-10 14:50:00 +0530889 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
890 mode->hdisplay, mode->vdisplay, mode->vrefresh,
891 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
892 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900893
Rahul Sharma16844fb2013-06-10 14:50:00 +0530894 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900895 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500896 return MODE_BAD;
897
898 return MODE_OK;
899}
900
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100901static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500902 .get_modes = hdmi_get_modes,
903 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500904};
905
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300906static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500907{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300908 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500909 struct drm_connector *connector = &hdata->connector;
910 int ret;
911
Sean Pauld9716ee2014-01-30 16:19:29 -0500912 connector->interlace_allowed = true;
913 connector->polled = DRM_CONNECTOR_POLL_HPD;
914
915 ret = drm_connector_init(hdata->drm_dev, connector,
916 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
917 if (ret) {
918 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900919 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500920 }
921
922 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300923 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500924
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100925 if (hdata->bridge) {
926 encoder->bridge = hdata->bridge;
927 hdata->bridge->encoder = encoder;
928 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
929 if (ret)
930 DRM_ERROR("Failed to attach bridge\n");
931 }
932
933 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500934}
935
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300936static bool hdmi_mode_fixup(struct drm_encoder *encoder,
937 const struct drm_display_mode *mode,
938 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500939{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300940 struct drm_device *dev = encoder->dev;
941 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500942 struct drm_display_mode *m;
943 int mode_ok;
944
Sean Paulf041b252014-01-30 16:19:15 -0500945 drm_mode_set_crtcinfo(adjusted_mode, 0);
946
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300947 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
948 if (connector->encoder == encoder)
949 break;
950 }
951
952 if (connector->encoder != encoder)
953 return true;
954
Sean Pauld9716ee2014-01-30 16:19:29 -0500955 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500956
Sean Pauld9716ee2014-01-30 16:19:29 -0500957 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300958 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500959
960 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900961 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500962 */
963 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500964 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500965
Sean Pauld9716ee2014-01-30 16:19:29 -0500966 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500967 DRM_INFO("desired mode doesn't exist so\n");
968 DRM_INFO("use the most suitable mode among modes.\n");
969
970 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
971 m->hdisplay, m->vdisplay, m->vrefresh);
972
Sean Paul75626852014-01-30 16:19:16 -0500973 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500974 break;
975 }
976 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300977
978 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500979}
980
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200981static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900982{
983 u32 n, cts;
984
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200985 cts = (freq % 9) ? 27000 : 30000;
986 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900987
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200988 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
989 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
990 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200991 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900992}
993
994static void hdmi_audio_init(struct hdmi_context *hdata)
995{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +0530996 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900997 u32 data_num, bit_ch, sample_frq;
998 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900999
1000 sample_rate = 44100;
1001 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001002
1003 switch (bits_per_sample) {
1004 case 20:
1005 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001006 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001007 break;
1008 case 24:
1009 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001010 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001011 break;
1012 default:
1013 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001014 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001015 break;
1016 }
1017
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001018 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001019
1020 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1021 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1022 | HDMI_I2S_MUX_ENABLE);
1023
1024 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1025 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1026
1027 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1028
1029 sample_frq = (sample_rate == 44100) ? 0 :
1030 (sample_rate == 48000) ? 2 :
1031 (sample_rate == 32000) ? 3 :
1032 (sample_rate == 96000) ? 0xa : 0x0;
1033
1034 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1035 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1036
1037 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1038 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1039
1040 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1041 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1042 | HDMI_I2S_SEL_LRCK(6));
1043 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1044 | HDMI_I2S_SEL_SDATA2(4));
1045 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1046 | HDMI_I2S_SEL_SDATA2(2));
1047 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1048
1049 /* I2S_CON_1 & 2 */
1050 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1051 | HDMI_I2S_L_CH_LOW_POL);
1052 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1053 | HDMI_I2S_SET_BIT_CH(bit_ch)
1054 | HDMI_I2S_SET_SDATA_BIT(data_num)
1055 | HDMI_I2S_BASIC_FORMAT);
1056
1057 /* Configure register related to CUV information */
1058 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1059 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1060 | HDMI_I2S_COPYRIGHT
1061 | HDMI_I2S_LINEAR_PCM
1062 | HDMI_I2S_CONSUMER_FORMAT);
1063 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1064 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1065 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1066 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1067 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1068 HDMI_I2S_ORG_SMP_FREQ_44_1
1069 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1070 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1071
1072 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1073}
1074
1075static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1076{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001077 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001078 return;
1079
1080 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1081 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1082 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1083}
1084
Rahul Sharmabfa48422014-04-03 20:41:04 +05301085static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001086{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301087 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001088
Rahul Sharmabfa48422014-04-03 20:41:04 +05301089 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1090 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001091
Rahul Sharmabfa48422014-04-03 20:41:04 +05301092 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1093 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001094}
1095
1096static void hdmi_conf_init(struct hdmi_context *hdata)
1097{
Sean Paul77006a72013-01-16 10:17:20 -05001098 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001099 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1100 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001101
1102 /* choose HDMI mode */
1103 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1104 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001105 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301106 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001107 /* disable bluescreen */
1108 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001109
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001110 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001111 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1112 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1113 hdmi_reg_writeb(hdata, HDMI_CON_2,
1114 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1115 }
1116
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001117 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001118 /* choose bluescreen (fecal) color */
1119 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1120 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1121 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1122
1123 /* enable AVI packet every vsync, fixes purple line problem */
1124 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1125 /* force RGB, look to CEA-861-D, table 7 for more detail */
1126 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1127 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1128
1129 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1130 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1131 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1132 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001133 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301134
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001135 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001136 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1137 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001138}
1139
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001140static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1141{
1142 int tries;
1143
1144 for (tries = 0; tries < 10; ++tries) {
1145 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1146
1147 if (val & HDMI_PHY_STATUS_READY) {
1148 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1149 return;
1150 }
1151 usleep_range(10, 20);
1152 }
1153
1154 DRM_ERROR("PLL could not reach steady state\n");
1155}
1156
Rahul Sharma16844fb2013-06-10 14:50:00 +05301157static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001158{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001159 struct drm_display_mode *m = &hdata->current_mode;
1160 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001161
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001162 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1163 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1164 (m->htotal << 12) | m->vtotal);
1165
1166 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1167 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1168
1169 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1170 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1171
1172 val = (m->hsync_start - m->hdisplay - 2);
1173 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001174 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001175 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1176
1177 /*
1178 * Quirk requirement for exynos HDMI IP design,
1179 * 2 pixels less than the actual calculation for hsync_start
1180 * and end.
1181 */
1182
1183 /* Following values & calculations differ for different type of modes */
1184 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001185 val = ((m->vsync_end - m->vdisplay) / 2);
1186 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1187 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1188
1189 val = m->vtotal / 2;
1190 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1191 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1192
1193 val = (m->vtotal +
1194 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1195 val |= m->vtotal << 11;
1196 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1197
1198 val = ((m->vtotal / 2) + 7);
1199 val |= ((m->vtotal / 2) + 2) << 12;
1200 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1201
1202 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1203 val |= ((m->htotal / 2) +
1204 (m->hsync_start - m->hdisplay)) << 12;
1205 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1206
1207 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1208 (m->vtotal - m->vdisplay) / 2);
1209 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1210
1211 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1212 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001213 val = m->vtotal;
1214 val |= (m->vtotal - m->vdisplay) << 11;
1215 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1216
1217 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1218
1219 val = (m->vsync_end - m->vdisplay);
1220 val |= ((m->vsync_start - m->vdisplay) << 12);
1221 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1222
1223 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1224 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1225 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1226 m->vtotal - m->vdisplay);
1227 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001228 }
1229
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001230 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1231 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1232 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1233 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001234}
1235
Rahul Sharma16844fb2013-06-10 14:50:00 +05301236static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001237{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001238 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001239
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001240 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1241 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1242 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1243 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001244 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001245 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1246 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1247 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1248 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1249
1250 /*
1251 * Quirk requirement for exynos 5 HDMI IP design,
1252 * 2 pixels less than the actual calculation for hsync_start
1253 * and end.
1254 */
1255
1256 /* Following values & calculations differ for different type of modes */
1257 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001258 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1259 (m->vsync_end - m->vdisplay) / 2);
1260 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1261 (m->vsync_start - m->vdisplay) / 2);
1262 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1263 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1264 (m->vtotal - m->vdisplay) / 2);
1265 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1266 m->vtotal - m->vdisplay / 2);
1267 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1268 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1269 (m->vtotal / 2) + 7);
1270 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1271 (m->vtotal / 2) + 2);
1272 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1273 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1274 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1275 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1276 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1277 (m->vtotal - m->vdisplay) / 2);
1278 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1279 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1280 m->vtotal - m->vdisplay / 2);
1281 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1282 (m->vtotal / 2) + 1);
1283 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1284 (m->vtotal / 2) + 1);
1285 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1286 (m->vtotal / 2) + 1);
1287 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1288 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1289 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001290 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1291 m->vsync_end - m->vdisplay);
1292 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1293 m->vsync_start - m->vdisplay);
1294 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1295 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1296 m->vtotal - m->vdisplay);
1297 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1298 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1299 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1300 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1301 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1302 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1303 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1304 m->vtotal - m->vdisplay);
1305 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001306 }
1307
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001308 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1309 m->hsync_start - m->hdisplay - 2);
1310 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1311 m->hsync_end - m->hdisplay - 2);
1312 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1313 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1314 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1315 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1316 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1317 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1318 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1319 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1322 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1323 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1327 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1328 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1329 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001330
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001331 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1332 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1333 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1334 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001335 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1336 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001337}
1338
Rahul Sharma16844fb2013-06-10 14:50:00 +05301339static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001340{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001341 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301342 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001343 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301344 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001345
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001346 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001347}
1348
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001349static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1350{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001351 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1352 usleep_range(10000, 12000);
1353 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1354 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001355 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001356 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001357 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001358 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001359}
1360
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001361static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1362{
1363 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1364
1365 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1366 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1367}
1368
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001369static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1370{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001371 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001372 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001373
Andrzej Hajda4677f512016-03-23 14:15:12 +01001374 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1375 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001376 DRM_ERROR("failed to find hdmiphy conf\n");
1377 return;
1378 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001379 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1380
1381 hdmi_clk_set_parents(hdata, false);
1382
1383 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001384
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001385 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001386 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001387 if (ret) {
1388 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001389 return;
1390 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001391 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001392 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001393 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001394 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001395}
1396
1397static void hdmi_conf_apply(struct hdmi_context *hdata)
1398{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301399 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001400 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001401 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301402 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001403 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001404}
1405
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001406static void hdmi_mode_set(struct drm_encoder *encoder,
1407 struct drm_display_mode *mode,
1408 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001409{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001410 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001411 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001412
YoungJun Chocbc4c332013-06-12 10:44:40 +09001413 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1414 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001415 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001416 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001417
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001418 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001419}
1420
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001421static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1422{
1423 if (!hdata->sysreg)
1424 return;
1425
1426 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1427 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1428}
1429
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001430static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001431{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001432 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001433 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001434
Sean Paulaf65c802014-01-30 16:19:27 -05001435 pm_runtime_get_sync(hdata->dev);
1436
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001437 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001438 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1439
Rahul Sharma049d34e2014-05-20 10:36:05 +05301440 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1441 PMU_HDMI_PHY_ENABLE_BIT, 1);
1442
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001443 hdmi_set_refclk(hdata, true);
1444
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001445 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1446
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001447 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001448
1449 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001450}
1451
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001452static void hdmiphy_disable(struct hdmi_context *hdata)
1453{
1454 if (!hdata->powered)
1455 return;
1456
1457 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1458
1459 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1460
1461 hdmi_set_refclk(hdata, false);
1462
1463 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1464 PMU_HDMI_PHY_ENABLE_BIT, 0);
1465
1466 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1467
1468 pm_runtime_put_sync(hdata->dev);
1469
1470 hdata->powered = false;
1471}
1472
1473static void hdmi_enable(struct drm_encoder *encoder)
1474{
1475 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1476
1477 hdmiphy_enable(hdata);
1478 hdmi_conf_apply(hdata);
1479}
1480
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001481static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001482{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001483 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001484 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001485 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001486
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001487 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001488 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001489
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001490 /*
1491 * The SFRs of VP and Mixer are updated by Vertical Sync of
1492 * Timing generator which is a part of HDMI so the sequence
1493 * to disable TV Subsystem should be as following,
1494 * VP -> Mixer -> HDMI
1495 *
1496 * Below codes will try to disable Mixer and VP(if used)
1497 * prior to disabling HDMI.
1498 */
1499 if (crtc)
1500 funcs = crtc->helper_private;
1501 if (funcs && funcs->disable)
1502 (*funcs->disable)(crtc);
1503
Sean Paul724fd142014-05-09 15:05:10 +09001504 cancel_delayed_work(&hdata->hotplug_work);
1505
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001506 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001507}
1508
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001509static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001510 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001511 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001512 .enable = hdmi_enable,
1513 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001514};
1515
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001516static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001517 .destroy = drm_encoder_cleanup,
1518};
1519
Sean Paul724fd142014-05-09 15:05:10 +09001520static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001521{
Sean Paul724fd142014-05-09 15:05:10 +09001522 struct hdmi_context *hdata;
1523
1524 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001525
Sean Paul45517892014-01-30 16:19:05 -05001526 if (hdata->drm_dev)
1527 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001528}
1529
1530static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1531{
1532 struct hdmi_context *hdata = arg;
1533
1534 mod_delayed_work(system_wq, &hdata->hotplug_work,
1535 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001536
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001537 return IRQ_HANDLED;
1538}
1539
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001540static int hdmi_clks_get(struct hdmi_context *hdata,
1541 const struct string_array_spec *names,
1542 struct clk **clks)
1543{
1544 struct device *dev = hdata->dev;
1545 int i;
1546
1547 for (i = 0; i < names->count; ++i) {
1548 struct clk *clk = devm_clk_get(dev, names->data[i]);
1549
1550 if (IS_ERR(clk)) {
1551 int ret = PTR_ERR(clk);
1552
1553 dev_err(dev, "Cannot get clock %s, %d\n",
1554 names->data[i], ret);
1555
1556 return ret;
1557 }
1558
1559 clks[i] = clk;
1560 }
1561
1562 return 0;
1563}
1564
1565static int hdmi_clk_init(struct hdmi_context *hdata)
1566{
1567 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1568 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1569 struct device *dev = hdata->dev;
1570 struct clk **clks;
1571 int ret;
1572
1573 if (!count)
1574 return 0;
1575
1576 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1577 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001578 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001579
1580 hdata->clk_gates = clks;
1581 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1582
1583 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1584 if (ret)
1585 return ret;
1586
1587 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1588}
1589
1590
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001591static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1592{
1593 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1594 phy_clk);
1595
1596 if (enable)
1597 hdmiphy_enable(hdata);
1598 else
1599 hdmiphy_disable(hdata);
1600}
1601
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001602static int hdmi_bridge_init(struct hdmi_context *hdata)
1603{
1604 struct device *dev = hdata->dev;
1605 struct device_node *ep, *np;
1606
1607 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1608 if (!ep)
1609 return 0;
1610
1611 np = of_graph_get_remote_port_parent(ep);
1612 of_node_put(ep);
1613 if (!np) {
1614 DRM_ERROR("failed to get remote port parent");
1615 return -EINVAL;
1616 }
1617
1618 hdata->bridge = of_drm_find_bridge(np);
1619 of_node_put(np);
1620
1621 if (!hdata->bridge)
1622 return -EPROBE_DEFER;
1623
1624 return 0;
1625}
1626
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001627static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001628{
1629 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001630 int i, ret;
1631
1632 DRM_DEBUG_KMS("HDMI resource init\n");
1633
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001634 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1635 if (IS_ERR(hdata->hpd_gpio)) {
1636 DRM_ERROR("cannot get hpd gpio property\n");
1637 return PTR_ERR(hdata->hpd_gpio);
1638 }
1639
1640 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1641 if (hdata->irq < 0) {
1642 DRM_ERROR("failed to get GPIO irq\n");
1643 return hdata->irq;
1644 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001645
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001646 ret = hdmi_clk_init(hdata);
1647 if (ret)
1648 return ret;
1649
1650 ret = hdmi_clk_set_parents(hdata, false);
1651 if (ret)
1652 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001653
Milo Kimc0d656d2016-08-31 15:14:27 +09001654 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001655 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001656
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001657 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001658 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001659 if (ret != -EPROBE_DEFER)
1660 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001661 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001662 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001663
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001664 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001665
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001666 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1667 if (IS_ERR(hdata->reg_hdmi_en))
1668 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001669
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001670 ret = regulator_enable(hdata->reg_hdmi_en);
1671 if (ret) {
1672 DRM_ERROR("failed to enable hdmi-en regulator\n");
1673 return ret;
1674 }
1675 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001676
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001677 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001678}
1679
Rahul Sharma22c4f422012-10-04 20:48:55 +05301680static struct of_device_id hdmi_match_types[] = {
1681 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001682 .compatible = "samsung,exynos4210-hdmi",
1683 .data = &exynos4210_hdmi_driver_data,
1684 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301685 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001686 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301687 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301688 .compatible = "samsung,exynos5420-hdmi",
1689 .data = &exynos5420_hdmi_driver_data,
1690 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001691 .compatible = "samsung,exynos5433-hdmi",
1692 .data = &exynos5433_hdmi_driver_data,
1693 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301694 /* end node */
1695 }
1696};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001697MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301698
Inki Daef37cd5e2014-05-09 14:25:20 +09001699static int hdmi_bind(struct device *dev, struct device *master, void *data)
1700{
1701 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001702 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001703 struct drm_encoder *encoder = &hdata->encoder;
Shawn Guo64b0e1d2017-02-07 17:16:20 +08001704 struct exynos_drm_crtc *exynos_crtc;
1705 struct drm_crtc *crtc;
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001706 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001707
Inki Daef37cd5e2014-05-09 14:25:20 +09001708 hdata->drm_dev = drm_dev;
1709
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001710 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1711 EXYNOS_DISPLAY_TYPE_HDMI);
1712 if (pipe < 0)
1713 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001714
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001715 hdata->phy_clk.enable = hdmiphy_clk_enable;
1716
Shawn Guo64b0e1d2017-02-07 17:16:20 +08001717 crtc = drm_crtc_from_index(drm_dev, pipe);
1718 exynos_crtc = to_exynos_crtc(crtc);
1719 exynos_crtc->pipe_clk = &hdata->phy_clk;
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001720
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001721 encoder->possible_crtcs = 1 << pipe;
1722
1723 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1724
1725 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001726 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001727
1728 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1729
1730 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001731 if (ret) {
1732 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001733 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001734 return ret;
1735 }
1736
1737 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001738}
1739
1740static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1741{
Inki Daef37cd5e2014-05-09 14:25:20 +09001742}
1743
1744static const struct component_ops hdmi_component_ops = {
1745 .bind = hdmi_bind,
1746 .unbind = hdmi_unbind,
1747};
1748
Milo Kim1caa3602016-08-31 15:14:25 +09001749static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001750{
1751 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1752 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001753 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001754
1755 np = of_find_compatible_node(NULL, NULL, compatible_str);
1756 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001757 np = of_get_next_parent(np);
1758 else
1759 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001760
Milo Kim1caa3602016-08-31 15:14:25 +09001761 if (!np) {
1762 DRM_ERROR("Failed to find ddc node in device tree\n");
1763 return -ENODEV;
1764 }
1765
1766 adpt = of_find_i2c_adapter_by_node(np);
1767 of_node_put(np);
1768
1769 if (!adpt) {
1770 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1771 return -EPROBE_DEFER;
1772 }
1773
1774 hdata->ddc_adpt = adpt;
1775
1776 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001777}
1778
Milo Kimb5413022016-08-31 15:14:26 +09001779static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001780{
1781 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001782 struct device_node *np;
1783 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001784
Milo Kimb5413022016-08-31 15:14:26 +09001785 np = of_find_compatible_node(NULL, NULL, compatible_str);
1786 if (!np) {
1787 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1788 if (!np) {
1789 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1790 return -ENODEV;
1791 }
1792 }
1793
1794 if (hdata->drv_data->is_apb_phy) {
1795 hdata->regs_hdmiphy = of_iomap(np, 0);
1796 if (!hdata->regs_hdmiphy) {
1797 DRM_ERROR("failed to ioremap hdmi phy\n");
1798 ret = -ENOMEM;
1799 goto out;
1800 }
1801 } else {
1802 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1803 if (!hdata->hdmiphy_port) {
1804 DRM_INFO("Failed to get hdmi phy i2c client\n");
1805 ret = -EPROBE_DEFER;
1806 goto out;
1807 }
1808 }
1809
1810out:
1811 of_node_put(np);
1812 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001813}
1814
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001815static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001816{
1817 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001818 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001819 struct resource *res;
1820 int ret;
1821
Andrzej Hajda930865f2014-11-17 09:54:20 +01001822 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1823 if (!hdata)
1824 return -ENOMEM;
1825
Marek Szyprowski57a64122016-04-01 15:17:44 +02001826 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001827
Andrzej Hajda930865f2014-11-17 09:54:20 +01001828 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001829
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001830 hdata->dev = dev;
1831
1832 ret = hdmi_resources_init(hdata);
1833 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001834 if (ret != -EPROBE_DEFER)
1835 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001836 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001837 }
1838
1839 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001840 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001841 if (IS_ERR(hdata->regs)) {
1842 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001843 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001844 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001845
Milo Kim1caa3602016-08-31 15:14:25 +09001846 ret = hdmi_get_ddc_adapter(hdata);
1847 if (ret)
1848 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001849
Milo Kimb5413022016-08-31 15:14:26 +09001850 ret = hdmi_get_phy_io(hdata);
1851 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001852 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001853
Sean Paul724fd142014-05-09 15:05:10 +09001854 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1855
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001856 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001857 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001858 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001859 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001860 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001861 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001862 goto err_hdmiphy;
1863 }
1864
Rahul Sharma049d34e2014-05-20 10:36:05 +05301865 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1866 "samsung,syscon-phandle");
1867 if (IS_ERR(hdata->pmureg)) {
1868 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001869 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301870 goto err_hdmiphy;
1871 }
1872
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001873 if (hdata->drv_data->has_sysreg) {
1874 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1875 "samsung,sysreg-phandle");
1876 if (IS_ERR(hdata->sysreg)) {
1877 DRM_ERROR("sysreg regmap lookup failed.\n");
1878 ret = -EPROBE_DEFER;
1879 goto err_hdmiphy;
1880 }
1881 }
1882
Sean Paulaf65c802014-01-30 16:19:27 -05001883 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001884
Inki Daedf5225b2014-05-29 18:28:02 +09001885 ret = component_add(&pdev->dev, &hdmi_component_ops);
1886 if (ret)
1887 goto err_disable_pm_runtime;
1888
1889 return ret;
1890
1891err_disable_pm_runtime:
1892 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001893
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001894err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001895 if (hdata->hdmiphy_port)
1896 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301897 if (hdata->regs_hdmiphy)
1898 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001899err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001900 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001901
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001902 return ret;
1903}
1904
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001905static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001906{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001907 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001908
Sean Paul724fd142014-05-09 15:05:10 +09001909 cancel_delayed_work_sync(&hdata->hotplug_work);
1910
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001911 component_del(&pdev->dev, &hdmi_component_ops);
1912
1913 pm_runtime_disable(&pdev->dev);
1914
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001915 if (!IS_ERR(hdata->reg_hdmi_en))
1916 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001917
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001918 if (hdata->hdmiphy_port)
1919 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001920
Arvind Yadavd7420002016-10-19 15:34:16 +05301921 if (hdata->regs_hdmiphy)
1922 iounmap(hdata->regs_hdmiphy);
1923
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001924 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001925
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001926 return 0;
1927}
1928
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001929#ifdef CONFIG_PM
1930static int exynos_hdmi_suspend(struct device *dev)
1931{
1932 struct hdmi_context *hdata = dev_get_drvdata(dev);
1933
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001934 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001935
1936 return 0;
1937}
1938
1939static int exynos_hdmi_resume(struct device *dev)
1940{
1941 struct hdmi_context *hdata = dev_get_drvdata(dev);
1942 int ret;
1943
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001944 ret = hdmi_clk_enable_gates(hdata);
1945 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001946 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001947
1948 return 0;
1949}
1950#endif
1951
1952static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1953 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1954};
1955
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001956struct platform_driver hdmi_driver = {
1957 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001958 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001959 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301960 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001961 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001962 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301963 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001964 },
1965};