blob: bef8965c9f5371c020b2ffe6d298d185c433867d [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>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053038#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090039#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053040#include <linux/mfd/syscon.h>
41#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090042
43#include <drm/exynos_drm.h>
44
45#include "exynos_drm_drv.h"
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;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900136};
137
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300138static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100139{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900140 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100141}
142
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200143static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
144{
145 return container_of(c, struct hdmi_context, connector);
146}
147
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900148static const struct hdmiphy_config hdmiphy_v13_configs[] = {
149 {
150 .pixel_clock = 27000000,
151 .conf = {
152 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
153 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
154 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200155 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900156 },
157 },
158 {
159 .pixel_clock = 27027000,
160 .conf = {
161 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
162 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
163 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200164 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900165 },
166 },
167 {
168 .pixel_clock = 74176000,
169 .conf = {
170 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
171 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
172 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200173 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900174 },
175 },
176 {
177 .pixel_clock = 74250000,
178 .conf = {
179 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
180 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
181 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200182 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900183 },
184 },
185 {
186 .pixel_clock = 148500000,
187 .conf = {
188 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
189 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
190 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200191 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900192 },
193 },
194};
195
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500196static const struct hdmiphy_config hdmiphy_v14_configs[] = {
197 {
198 .pixel_clock = 25200000,
199 .conf = {
200 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
201 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
202 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
203 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
204 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900205 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500206 {
207 .pixel_clock = 27000000,
208 .conf = {
209 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
210 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
211 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
212 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
213 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900214 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500215 {
216 .pixel_clock = 27027000,
217 .conf = {
218 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
219 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
220 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200221 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500222 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900223 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500224 {
225 .pixel_clock = 36000000,
226 .conf = {
227 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
228 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
229 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
230 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
231 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900232 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500233 {
234 .pixel_clock = 40000000,
235 .conf = {
236 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
237 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
238 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
239 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
240 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900241 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500242 {
243 .pixel_clock = 65000000,
244 .conf = {
245 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
246 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
247 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
248 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
249 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900250 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500251 {
Shirish Se1d883c2014-03-13 14:28:27 +0900252 .pixel_clock = 71000000,
253 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530254 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
255 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
256 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900257 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
258 },
259 },
260 {
261 .pixel_clock = 73250000,
262 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530263 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
264 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
265 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900266 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
267 },
268 },
269 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500270 .pixel_clock = 74176000,
271 .conf = {
272 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
273 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
274 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
275 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
276 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900277 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500278 {
279 .pixel_clock = 74250000,
280 .conf = {
281 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
282 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
283 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200284 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500285 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900286 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500287 {
288 .pixel_clock = 83500000,
289 .conf = {
290 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
291 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
292 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
293 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
294 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900295 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500296 {
297 .pixel_clock = 106500000,
298 .conf = {
299 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
300 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
301 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
302 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
303 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900304 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500305 {
306 .pixel_clock = 108000000,
307 .conf = {
308 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
309 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
310 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
311 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
312 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900313 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500314 {
Shirish Se1d883c2014-03-13 14:28:27 +0900315 .pixel_clock = 115500000,
316 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530317 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
318 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
319 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900320 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
321 },
322 },
323 {
324 .pixel_clock = 119000000,
325 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530326 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
327 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
328 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900329 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
330 },
331 },
332 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500333 .pixel_clock = 146250000,
334 .conf = {
335 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
336 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
337 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
338 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
339 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900340 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500341 {
342 .pixel_clock = 148500000,
343 .conf = {
344 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
345 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
346 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200347 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500348 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900349 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900350};
351
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530352static const struct hdmiphy_config hdmiphy_5420_configs[] = {
353 {
354 .pixel_clock = 25200000,
355 .conf = {
356 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
357 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
358 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
359 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
360 },
361 },
362 {
363 .pixel_clock = 27000000,
364 .conf = {
365 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
366 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
367 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
368 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
369 },
370 },
371 {
372 .pixel_clock = 27027000,
373 .conf = {
374 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
375 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
376 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
377 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
378 },
379 },
380 {
381 .pixel_clock = 36000000,
382 .conf = {
383 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
384 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
385 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
386 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
387 },
388 },
389 {
390 .pixel_clock = 40000000,
391 .conf = {
392 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
393 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
394 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
395 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
396 },
397 },
398 {
399 .pixel_clock = 65000000,
400 .conf = {
401 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
402 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
403 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
404 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
405 },
406 },
407 {
408 .pixel_clock = 71000000,
409 .conf = {
410 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
411 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
412 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
413 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
414 },
415 },
416 {
417 .pixel_clock = 73250000,
418 .conf = {
419 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
420 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
421 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
422 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
423 },
424 },
425 {
426 .pixel_clock = 74176000,
427 .conf = {
428 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
429 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
430 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
431 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
432 },
433 },
434 {
435 .pixel_clock = 74250000,
436 .conf = {
437 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
438 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
439 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
440 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
441 },
442 },
443 {
444 .pixel_clock = 83500000,
445 .conf = {
446 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
447 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
448 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
449 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
450 },
451 },
452 {
453 .pixel_clock = 88750000,
454 .conf = {
455 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
456 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
457 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
458 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
459 },
460 },
461 {
462 .pixel_clock = 106500000,
463 .conf = {
464 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
465 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
466 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
467 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
468 },
469 },
470 {
471 .pixel_clock = 108000000,
472 .conf = {
473 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
474 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
475 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
476 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
477 },
478 },
479 {
480 .pixel_clock = 115500000,
481 .conf = {
482 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
483 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
484 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
485 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
486 },
487 },
488 {
489 .pixel_clock = 146250000,
490 .conf = {
491 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
492 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
493 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
494 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
495 },
496 },
497 {
498 .pixel_clock = 148500000,
499 .conf = {
500 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
501 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
502 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
503 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
504 },
505 },
506};
507
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900508static const struct hdmiphy_config hdmiphy_5433_configs[] = {
509 {
510 .pixel_clock = 27000000,
511 .conf = {
512 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46,
513 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
514 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
515 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
516 },
517 },
518 {
519 .pixel_clock = 27027000,
520 .conf = {
521 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
522 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5,
523 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
524 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
525 },
526 },
527 {
528 .pixel_clock = 40000000,
529 .conf = {
530 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
531 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
532 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
533 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
534 },
535 },
536 {
537 .pixel_clock = 50000000,
538 .conf = {
539 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
540 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
541 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
542 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
543 },
544 },
545 {
546 .pixel_clock = 65000000,
547 .conf = {
548 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
549 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
550 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
551 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
552 },
553 },
554 {
555 .pixel_clock = 74176000,
556 .conf = {
557 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
558 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
559 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
560 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
561 },
562 },
563 {
564 .pixel_clock = 74250000,
565 .conf = {
566 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
567 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
568 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
569 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
570 },
571 },
572 {
573 .pixel_clock = 108000000,
574 .conf = {
575 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
576 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
577 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
578 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
579 },
580 },
581 {
582 .pixel_clock = 148500000,
583 .conf = {
584 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
585 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
586 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
587 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
588 },
589 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100590 {
591 .pixel_clock = 297000000,
592 .conf = {
593 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
594 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
595 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
596 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
597 },
598 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900599};
600
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100601static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900602 "hdmi", "sclk_hdmi"
603};
604
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100605static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900606 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
607};
608
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900609static const char * const hdmi_clk_gates5433[] = {
610 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
611};
612
613static const char * const hdmi_clk_muxes5433[] = {
614 "oscclk", "tmds_clko", "tmds_clko_user",
615 "oscclk", "pixel_clko", "pixel_clko_user"
616};
617
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900618static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
619 .type = HDMI_TYPE13,
620 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900621 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
622 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530623};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900624
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100625static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900626 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100627 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900628 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
629 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900630};
631
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900632static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
633 .type = HDMI_TYPE14,
634 .is_apb_phy = 1,
635 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900636 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
637 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200638};
639
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900640static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
641 .type = HDMI_TYPE14,
642 .is_apb_phy = 1,
643 .has_sysreg = 1,
644 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
645 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
646 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
647};
648
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200649static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
650{
651 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
652 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
653 return reg_id;
654}
655
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900656static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
657{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200658 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900659}
660
661static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
662 u32 reg_id, u8 value)
663{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200664 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900665}
666
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200667static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
668 int bytes, u32 val)
669{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200670 reg_id = hdmi_map_reg(hdata, reg_id);
671
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200672 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200673 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200674 val >>= 8;
675 reg_id += 4;
676 }
677}
678
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100679static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
680 u8 *buf, int size)
681{
682 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
683 writel(*buf++, hdata->regs + reg_id);
684}
685
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900686static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
687 u32 reg_id, u32 value, u32 mask)
688{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200689 u32 old;
690
691 reg_id = hdmi_map_reg(hdata, reg_id);
692 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900693 value = (value & mask) | (old & ~mask);
694 writel(value, hdata->regs + reg_id);
695}
696
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900697static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
698 u32 reg_offset, const u8 *buf, u32 len)
699{
700 if ((reg_offset + len) > 32)
701 return -EINVAL;
702
703 if (hdata->hdmiphy_port) {
704 int ret;
705
706 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
707 if (ret == len)
708 return 0;
709 return ret;
710 } else {
711 int i;
712 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200713 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900714 ((reg_offset + i)<<2));
715 return 0;
716 }
717}
718
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900719static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
720{
721 int i, ret;
722
723 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
724 ret = clk_prepare_enable(hdata->clk_gates[i]);
725 if (!ret)
726 continue;
727
728 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
729 hdata->drv_data->clk_gates.data[i], ret);
730 while (i--)
731 clk_disable_unprepare(hdata->clk_gates[i]);
732 return ret;
733 }
734
735 return 0;
736}
737
738static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
739{
740 int i = hdata->drv_data->clk_gates.count;
741
742 while (i--)
743 clk_disable_unprepare(hdata->clk_gates[i]);
744}
745
746static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
747{
748 struct device *dev = hdata->dev;
749 int ret = 0;
750 int i;
751
752 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
753 struct clk **c = &hdata->clk_muxes[i];
754
755 ret = clk_set_parent(c[2], c[to_phy]);
756 if (!ret)
757 continue;
758
759 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
760 hdata->drv_data->clk_muxes.data[i + 2],
761 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
762 }
763
764 return ret;
765}
766
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100767static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530768{
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100769 union hdmi_infoframe frm;
770 u8 buf[25];
771 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530772
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530773 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530774 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
775 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100776 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
777 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530778 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
779 return;
780 }
781
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100782 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
783 &hdata->current_mode);
784 if (!ret)
785 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
786 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530787 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100788 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
789 } else {
790 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
791 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530792
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100793 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
794 &hdata->current_mode);
795 if (!ret)
796 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
797 sizeof(buf));
798 if (ret > 0) {
799 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
800 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, ret);
801 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530802
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100803 ret = hdmi_audio_infoframe_init(&frm.audio);
804 if (!ret) {
805 frm.audio.channels = 2;
806 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
807 }
808 if (ret > 0) {
809 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
810 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530811 }
812}
813
Sean Pauld9716ee2014-01-30 16:19:29 -0500814static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
815 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500816{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200817 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500818
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200819 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200820 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530821
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200822 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500823}
824
Sean Pauld9716ee2014-01-30 16:19:29 -0500825static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900826{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200827 drm_connector_unregister(connector);
828 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900829}
830
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100831static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300832 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500833 .fill_modes = drm_helper_probe_single_connector_modes,
834 .detect = hdmi_detect,
835 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300836 .reset = drm_atomic_helper_connector_reset,
837 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
838 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500839};
840
841static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900842{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200843 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500844 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200845 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900846
Inki Dae8fa04aa2014-03-13 16:38:31 +0900847 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500848 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900849
Inki Dae8fa04aa2014-03-13 16:38:31 +0900850 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500851 if (!edid)
852 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900853
Sean Pauld9716ee2014-01-30 16:19:29 -0500854 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500855 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
856 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500857 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500858
Sean Pauld9716ee2014-01-30 16:19:29 -0500859 drm_mode_connector_update_edid_property(connector, edid);
860
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200861 ret = drm_add_edid_modes(connector, edid);
862
863 kfree(edid);
864
865 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900866}
867
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900868static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900869{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100870 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900871 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900872
Andrzej Hajda65e98032015-11-02 14:16:41 +0100873 for (i = 0; i < confs->count; i++)
874 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500875 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500876
877 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
878 return -EINVAL;
879}
880
Sean Pauld9716ee2014-01-30 16:19:29 -0500881static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500882 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900883{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200884 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900885 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900886
Rahul Sharma16844fb2013-06-10 14:50:00 +0530887 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
888 mode->hdisplay, mode->vdisplay, mode->vrefresh,
889 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
890 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900891
Rahul Sharma16844fb2013-06-10 14:50:00 +0530892 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900893 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500894 return MODE_BAD;
895
896 return MODE_OK;
897}
898
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100899static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500900 .get_modes = hdmi_get_modes,
901 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500902};
903
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300904static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500905{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300906 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500907 struct drm_connector *connector = &hdata->connector;
908 int ret;
909
Sean Pauld9716ee2014-01-30 16:19:29 -0500910 connector->interlace_allowed = true;
911 connector->polled = DRM_CONNECTOR_POLL_HPD;
912
913 ret = drm_connector_init(hdata->drm_dev, connector,
914 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
915 if (ret) {
916 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900917 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500918 }
919
920 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100921 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300922 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500923
924 return 0;
925}
926
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300927static bool hdmi_mode_fixup(struct drm_encoder *encoder,
928 const struct drm_display_mode *mode,
929 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500930{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300931 struct drm_device *dev = encoder->dev;
932 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500933 struct drm_display_mode *m;
934 int mode_ok;
935
Sean Paulf041b252014-01-30 16:19:15 -0500936 drm_mode_set_crtcinfo(adjusted_mode, 0);
937
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300938 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
939 if (connector->encoder == encoder)
940 break;
941 }
942
943 if (connector->encoder != encoder)
944 return true;
945
Sean Pauld9716ee2014-01-30 16:19:29 -0500946 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500947
Sean Pauld9716ee2014-01-30 16:19:29 -0500948 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300949 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500950
951 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900952 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500953 */
954 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500955 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500956
Sean Pauld9716ee2014-01-30 16:19:29 -0500957 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500958 DRM_INFO("desired mode doesn't exist so\n");
959 DRM_INFO("use the most suitable mode among modes.\n");
960
961 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
962 m->hdisplay, m->vdisplay, m->vrefresh);
963
Sean Paul75626852014-01-30 16:19:16 -0500964 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500965 break;
966 }
967 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300968
969 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500970}
971
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200972static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900973{
974 u32 n, cts;
975
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200976 cts = (freq % 9) ? 27000 : 30000;
977 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900978
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200979 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
980 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
981 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200982 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900983}
984
985static void hdmi_audio_init(struct hdmi_context *hdata)
986{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +0530987 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900988 u32 data_num, bit_ch, sample_frq;
989 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900990
991 sample_rate = 44100;
992 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900993
994 switch (bits_per_sample) {
995 case 20:
996 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900997 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900998 break;
999 case 24:
1000 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001001 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001002 break;
1003 default:
1004 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001005 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001006 break;
1007 }
1008
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001009 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001010
1011 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1012 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1013 | HDMI_I2S_MUX_ENABLE);
1014
1015 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1016 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1017
1018 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1019
1020 sample_frq = (sample_rate == 44100) ? 0 :
1021 (sample_rate == 48000) ? 2 :
1022 (sample_rate == 32000) ? 3 :
1023 (sample_rate == 96000) ? 0xa : 0x0;
1024
1025 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1026 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1027
1028 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1029 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1030
1031 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1032 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1033 | HDMI_I2S_SEL_LRCK(6));
1034 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1035 | HDMI_I2S_SEL_SDATA2(4));
1036 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1037 | HDMI_I2S_SEL_SDATA2(2));
1038 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1039
1040 /* I2S_CON_1 & 2 */
1041 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1042 | HDMI_I2S_L_CH_LOW_POL);
1043 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1044 | HDMI_I2S_SET_BIT_CH(bit_ch)
1045 | HDMI_I2S_SET_SDATA_BIT(data_num)
1046 | HDMI_I2S_BASIC_FORMAT);
1047
1048 /* Configure register related to CUV information */
1049 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1050 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1051 | HDMI_I2S_COPYRIGHT
1052 | HDMI_I2S_LINEAR_PCM
1053 | HDMI_I2S_CONSUMER_FORMAT);
1054 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1055 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1056 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1057 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1058 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1059 HDMI_I2S_ORG_SMP_FREQ_44_1
1060 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1061 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1062
1063 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1064}
1065
1066static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1067{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001068 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001069 return;
1070
1071 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1072 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1073 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1074}
1075
Rahul Sharmabfa48422014-04-03 20:41:04 +05301076static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001077{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301078 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001079
Rahul Sharmabfa48422014-04-03 20:41:04 +05301080 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1081 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001082
Rahul Sharmabfa48422014-04-03 20:41:04 +05301083 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1084 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001085}
1086
1087static void hdmi_conf_init(struct hdmi_context *hdata)
1088{
Sean Paul77006a72013-01-16 10:17:20 -05001089 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001090 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1091 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001092
1093 /* choose HDMI mode */
1094 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1095 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001096 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301097 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001098 /* disable bluescreen */
1099 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001100
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001101 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001102 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1103 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1104 hdmi_reg_writeb(hdata, HDMI_CON_2,
1105 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1106 }
1107
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001108 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001109 /* choose bluescreen (fecal) color */
1110 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1111 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1112 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1113
1114 /* enable AVI packet every vsync, fixes purple line problem */
1115 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1116 /* force RGB, look to CEA-861-D, table 7 for more detail */
1117 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1118 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1119
1120 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1121 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1122 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1123 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001124 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301125
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001126 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001127 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1128 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001129}
1130
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001131static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1132{
1133 int tries;
1134
1135 for (tries = 0; tries < 10; ++tries) {
1136 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1137
1138 if (val & HDMI_PHY_STATUS_READY) {
1139 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1140 return;
1141 }
1142 usleep_range(10, 20);
1143 }
1144
1145 DRM_ERROR("PLL could not reach steady state\n");
1146}
1147
Rahul Sharma16844fb2013-06-10 14:50:00 +05301148static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001149{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001150 struct drm_display_mode *m = &hdata->current_mode;
1151 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001152
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001153 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1154 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1155 (m->htotal << 12) | m->vtotal);
1156
1157 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1158 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1159
1160 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1161 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1162
1163 val = (m->hsync_start - m->hdisplay - 2);
1164 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001165 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001166 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1167
1168 /*
1169 * Quirk requirement for exynos HDMI IP design,
1170 * 2 pixels less than the actual calculation for hsync_start
1171 * and end.
1172 */
1173
1174 /* Following values & calculations differ for different type of modes */
1175 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001176 val = ((m->vsync_end - m->vdisplay) / 2);
1177 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1178 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1179
1180 val = m->vtotal / 2;
1181 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1182 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1183
1184 val = (m->vtotal +
1185 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1186 val |= m->vtotal << 11;
1187 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1188
1189 val = ((m->vtotal / 2) + 7);
1190 val |= ((m->vtotal / 2) + 2) << 12;
1191 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1192
1193 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1194 val |= ((m->htotal / 2) +
1195 (m->hsync_start - m->hdisplay)) << 12;
1196 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1197
1198 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1199 (m->vtotal - m->vdisplay) / 2);
1200 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1201
1202 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1203 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001204 val = m->vtotal;
1205 val |= (m->vtotal - m->vdisplay) << 11;
1206 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1207
1208 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1209
1210 val = (m->vsync_end - m->vdisplay);
1211 val |= ((m->vsync_start - m->vdisplay) << 12);
1212 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1213
1214 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1215 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1216 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1217 m->vtotal - m->vdisplay);
1218 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001219 }
1220
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001221 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1222 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1223 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1224 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001225}
1226
Rahul Sharma16844fb2013-06-10 14:50:00 +05301227static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001228{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001229 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001230
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001231 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1232 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1233 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1234 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001235 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001236 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1237 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1238 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1239 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1240
1241 /*
1242 * Quirk requirement for exynos 5 HDMI IP design,
1243 * 2 pixels less than the actual calculation for hsync_start
1244 * and end.
1245 */
1246
1247 /* Following values & calculations differ for different type of modes */
1248 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001249 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1250 (m->vsync_end - m->vdisplay) / 2);
1251 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1252 (m->vsync_start - m->vdisplay) / 2);
1253 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1254 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1255 (m->vtotal - m->vdisplay) / 2);
1256 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1257 m->vtotal - m->vdisplay / 2);
1258 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1259 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1260 (m->vtotal / 2) + 7);
1261 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1262 (m->vtotal / 2) + 2);
1263 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1264 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1265 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1266 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1267 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1268 (m->vtotal - m->vdisplay) / 2);
1269 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1270 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1271 m->vtotal - m->vdisplay / 2);
1272 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1273 (m->vtotal / 2) + 1);
1274 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1275 (m->vtotal / 2) + 1);
1276 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1277 (m->vtotal / 2) + 1);
1278 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1279 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1280 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001281 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1282 m->vsync_end - m->vdisplay);
1283 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1284 m->vsync_start - m->vdisplay);
1285 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1286 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1287 m->vtotal - m->vdisplay);
1288 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1289 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1290 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1291 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1292 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1293 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1294 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1295 m->vtotal - m->vdisplay);
1296 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001297 }
1298
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001299 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1300 m->hsync_start - m->hdisplay - 2);
1301 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1302 m->hsync_end - m->hdisplay - 2);
1303 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1304 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1306 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1307 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1308 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1309 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1310 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1311 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1312 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1313 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1314 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1315 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1316 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1317 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1318 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1319 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001321
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001322 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1323 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1324 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1325 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001326 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1327 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001328}
1329
Rahul Sharma16844fb2013-06-10 14:50:00 +05301330static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001331{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001332 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301333 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001334 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301335 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001336
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001337 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001338}
1339
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001340static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1341{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001342 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1343 usleep_range(10000, 12000);
1344 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1345 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001346 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001347 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001348 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001349 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001350}
1351
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001352static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1353{
1354 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1355
1356 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1357 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1358}
1359
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001360static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1361{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001362 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001363 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001364
Andrzej Hajda4677f512016-03-23 14:15:12 +01001365 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1366 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001367 DRM_ERROR("failed to find hdmiphy conf\n");
1368 return;
1369 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001370 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1371
1372 hdmi_clk_set_parents(hdata, false);
1373
1374 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001375
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001376 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001377 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001378 if (ret) {
1379 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001380 return;
1381 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001382 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001383 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001384 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001385 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001386}
1387
1388static void hdmi_conf_apply(struct hdmi_context *hdata)
1389{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301390 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001391 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001392 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301393 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001394 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001395}
1396
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001397static void hdmi_mode_set(struct drm_encoder *encoder,
1398 struct drm_display_mode *mode,
1399 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001400{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001401 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001402 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001403
YoungJun Chocbc4c332013-06-12 10:44:40 +09001404 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1405 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001406 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001407 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001408
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001409 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001410}
1411
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001412static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1413{
1414 if (!hdata->sysreg)
1415 return;
1416
1417 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1418 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1419}
1420
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001421static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001422{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001423 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001424 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001425
Sean Paulaf65c802014-01-30 16:19:27 -05001426 pm_runtime_get_sync(hdata->dev);
1427
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001428 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001429 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1430
Rahul Sharma049d34e2014-05-20 10:36:05 +05301431 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1432 PMU_HDMI_PHY_ENABLE_BIT, 1);
1433
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001434 hdmi_set_refclk(hdata, true);
1435
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001436 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1437
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001438 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001439
1440 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001441}
1442
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001443static void hdmiphy_disable(struct hdmi_context *hdata)
1444{
1445 if (!hdata->powered)
1446 return;
1447
1448 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1449
1450 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1451
1452 hdmi_set_refclk(hdata, false);
1453
1454 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1455 PMU_HDMI_PHY_ENABLE_BIT, 0);
1456
1457 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1458
1459 pm_runtime_put_sync(hdata->dev);
1460
1461 hdata->powered = false;
1462}
1463
1464static void hdmi_enable(struct drm_encoder *encoder)
1465{
1466 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1467
1468 hdmiphy_enable(hdata);
1469 hdmi_conf_apply(hdata);
1470}
1471
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001472static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001473{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001474 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001475 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001476 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001477
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001478 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001479 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001480
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001481 /*
1482 * The SFRs of VP and Mixer are updated by Vertical Sync of
1483 * Timing generator which is a part of HDMI so the sequence
1484 * to disable TV Subsystem should be as following,
1485 * VP -> Mixer -> HDMI
1486 *
1487 * Below codes will try to disable Mixer and VP(if used)
1488 * prior to disabling HDMI.
1489 */
1490 if (crtc)
1491 funcs = crtc->helper_private;
1492 if (funcs && funcs->disable)
1493 (*funcs->disable)(crtc);
1494
Sean Paul724fd142014-05-09 15:05:10 +09001495 cancel_delayed_work(&hdata->hotplug_work);
1496
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001497 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001498}
1499
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001500static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001501 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001502 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001503 .enable = hdmi_enable,
1504 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001505};
1506
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001507static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001508 .destroy = drm_encoder_cleanup,
1509};
1510
Sean Paul724fd142014-05-09 15:05:10 +09001511static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001512{
Sean Paul724fd142014-05-09 15:05:10 +09001513 struct hdmi_context *hdata;
1514
1515 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001516
Sean Paul45517892014-01-30 16:19:05 -05001517 if (hdata->drm_dev)
1518 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001519}
1520
1521static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1522{
1523 struct hdmi_context *hdata = arg;
1524
1525 mod_delayed_work(system_wq, &hdata->hotplug_work,
1526 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001527
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001528 return IRQ_HANDLED;
1529}
1530
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001531static int hdmi_clks_get(struct hdmi_context *hdata,
1532 const struct string_array_spec *names,
1533 struct clk **clks)
1534{
1535 struct device *dev = hdata->dev;
1536 int i;
1537
1538 for (i = 0; i < names->count; ++i) {
1539 struct clk *clk = devm_clk_get(dev, names->data[i]);
1540
1541 if (IS_ERR(clk)) {
1542 int ret = PTR_ERR(clk);
1543
1544 dev_err(dev, "Cannot get clock %s, %d\n",
1545 names->data[i], ret);
1546
1547 return ret;
1548 }
1549
1550 clks[i] = clk;
1551 }
1552
1553 return 0;
1554}
1555
1556static int hdmi_clk_init(struct hdmi_context *hdata)
1557{
1558 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1559 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1560 struct device *dev = hdata->dev;
1561 struct clk **clks;
1562 int ret;
1563
1564 if (!count)
1565 return 0;
1566
1567 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1568 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001569 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001570
1571 hdata->clk_gates = clks;
1572 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1573
1574 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1575 if (ret)
1576 return ret;
1577
1578 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1579}
1580
1581
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001582static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1583{
1584 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1585 phy_clk);
1586
1587 if (enable)
1588 hdmiphy_enable(hdata);
1589 else
1590 hdmiphy_disable(hdata);
1591}
1592
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001593static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001594{
1595 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001596 int i, ret;
1597
1598 DRM_DEBUG_KMS("HDMI resource init\n");
1599
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001600 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1601 if (IS_ERR(hdata->hpd_gpio)) {
1602 DRM_ERROR("cannot get hpd gpio property\n");
1603 return PTR_ERR(hdata->hpd_gpio);
1604 }
1605
1606 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1607 if (hdata->irq < 0) {
1608 DRM_ERROR("failed to get GPIO irq\n");
1609 return hdata->irq;
1610 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001611
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001612 ret = hdmi_clk_init(hdata);
1613 if (ret)
1614 return ret;
1615
1616 ret = hdmi_clk_set_parents(hdata, false);
1617 if (ret)
1618 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001619
Milo Kimc0d656d2016-08-31 15:14:27 +09001620 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001621 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001622
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001623 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001624 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001625 if (ret != -EPROBE_DEFER)
1626 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001627 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001628 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001629
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001630 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001631
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001632 if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001633 return 0;
1634
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001635 if (IS_ERR(hdata->reg_hdmi_en))
1636 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001637
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001638 ret = regulator_enable(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001639 if (ret)
1640 DRM_ERROR("failed to enable hdmi-en regulator\n");
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001641
Inki Daedf5225b2014-05-29 18:28:02 +09001642 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001643}
1644
Rahul Sharma22c4f422012-10-04 20:48:55 +05301645static struct of_device_id hdmi_match_types[] = {
1646 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001647 .compatible = "samsung,exynos4210-hdmi",
1648 .data = &exynos4210_hdmi_driver_data,
1649 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301650 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001651 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301652 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301653 .compatible = "samsung,exynos5420-hdmi",
1654 .data = &exynos5420_hdmi_driver_data,
1655 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001656 .compatible = "samsung,exynos5433-hdmi",
1657 .data = &exynos5433_hdmi_driver_data,
1658 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301659 /* end node */
1660 }
1661};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001662MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301663
Inki Daef37cd5e2014-05-09 14:25:20 +09001664static int hdmi_bind(struct device *dev, struct device *master, void *data)
1665{
1666 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001667 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001668 struct drm_encoder *encoder = &hdata->encoder;
1669 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001670
Inki Daef37cd5e2014-05-09 14:25:20 +09001671 hdata->drm_dev = drm_dev;
1672
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001673 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1674 EXYNOS_DISPLAY_TYPE_HDMI);
1675 if (pipe < 0)
1676 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001677
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001678 hdata->phy_clk.enable = hdmiphy_clk_enable;
1679
1680 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1681
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001682 encoder->possible_crtcs = 1 << pipe;
1683
1684 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1685
1686 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001687 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001688
1689 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1690
1691 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001692 if (ret) {
1693 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001694 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001695 return ret;
1696 }
1697
1698 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001699}
1700
1701static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1702{
Inki Daef37cd5e2014-05-09 14:25:20 +09001703}
1704
1705static const struct component_ops hdmi_component_ops = {
1706 .bind = hdmi_bind,
1707 .unbind = hdmi_unbind,
1708};
1709
Milo Kim1caa3602016-08-31 15:14:25 +09001710static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001711{
1712 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1713 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001714 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001715
1716 np = of_find_compatible_node(NULL, NULL, compatible_str);
1717 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001718 np = of_get_next_parent(np);
1719 else
1720 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001721
Milo Kim1caa3602016-08-31 15:14:25 +09001722 if (!np) {
1723 DRM_ERROR("Failed to find ddc node in device tree\n");
1724 return -ENODEV;
1725 }
1726
1727 adpt = of_find_i2c_adapter_by_node(np);
1728 of_node_put(np);
1729
1730 if (!adpt) {
1731 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1732 return -EPROBE_DEFER;
1733 }
1734
1735 hdata->ddc_adpt = adpt;
1736
1737 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001738}
1739
Milo Kimb5413022016-08-31 15:14:26 +09001740static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001741{
1742 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001743 struct device_node *np;
1744 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001745
Milo Kimb5413022016-08-31 15:14:26 +09001746 np = of_find_compatible_node(NULL, NULL, compatible_str);
1747 if (!np) {
1748 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1749 if (!np) {
1750 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1751 return -ENODEV;
1752 }
1753 }
1754
1755 if (hdata->drv_data->is_apb_phy) {
1756 hdata->regs_hdmiphy = of_iomap(np, 0);
1757 if (!hdata->regs_hdmiphy) {
1758 DRM_ERROR("failed to ioremap hdmi phy\n");
1759 ret = -ENOMEM;
1760 goto out;
1761 }
1762 } else {
1763 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1764 if (!hdata->hdmiphy_port) {
1765 DRM_INFO("Failed to get hdmi phy i2c client\n");
1766 ret = -EPROBE_DEFER;
1767 goto out;
1768 }
1769 }
1770
1771out:
1772 of_node_put(np);
1773 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001774}
1775
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001776static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001777{
1778 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001779 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001780 struct resource *res;
1781 int ret;
1782
Andrzej Hajda930865f2014-11-17 09:54:20 +01001783 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1784 if (!hdata)
1785 return -ENOMEM;
1786
Marek Szyprowski57a64122016-04-01 15:17:44 +02001787 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001788
Andrzej Hajda930865f2014-11-17 09:54:20 +01001789 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001790
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001791 hdata->dev = dev;
1792
1793 ret = hdmi_resources_init(hdata);
1794 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001795 if (ret != -EPROBE_DEFER)
1796 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001797 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001798 }
1799
1800 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001801 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001802 if (IS_ERR(hdata->regs)) {
1803 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001804 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001805 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001806
Milo Kim1caa3602016-08-31 15:14:25 +09001807 ret = hdmi_get_ddc_adapter(hdata);
1808 if (ret)
1809 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001810
Milo Kimb5413022016-08-31 15:14:26 +09001811 ret = hdmi_get_phy_io(hdata);
1812 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001813 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001814
Sean Paul724fd142014-05-09 15:05:10 +09001815 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1816
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001817 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001818 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001819 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001820 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001821 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001822 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001823 goto err_hdmiphy;
1824 }
1825
Rahul Sharma049d34e2014-05-20 10:36:05 +05301826 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1827 "samsung,syscon-phandle");
1828 if (IS_ERR(hdata->pmureg)) {
1829 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001830 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301831 goto err_hdmiphy;
1832 }
1833
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001834 if (hdata->drv_data->has_sysreg) {
1835 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1836 "samsung,sysreg-phandle");
1837 if (IS_ERR(hdata->sysreg)) {
1838 DRM_ERROR("sysreg regmap lookup failed.\n");
1839 ret = -EPROBE_DEFER;
1840 goto err_hdmiphy;
1841 }
1842 }
1843
Sean Paulaf65c802014-01-30 16:19:27 -05001844 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001845
Inki Daedf5225b2014-05-29 18:28:02 +09001846 ret = component_add(&pdev->dev, &hdmi_component_ops);
1847 if (ret)
1848 goto err_disable_pm_runtime;
1849
1850 return ret;
1851
1852err_disable_pm_runtime:
1853 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001854
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001855err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001856 if (hdata->hdmiphy_port)
1857 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301858 if (hdata->regs_hdmiphy)
1859 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001860err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001861 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001862
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001863 return ret;
1864}
1865
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001866static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001867{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001868 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001869
Sean Paul724fd142014-05-09 15:05:10 +09001870 cancel_delayed_work_sync(&hdata->hotplug_work);
1871
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001872 component_del(&pdev->dev, &hdmi_component_ops);
1873
1874 pm_runtime_disable(&pdev->dev);
1875
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001876 if (!IS_ERR(hdata->reg_hdmi_en))
1877 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001878
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001879 if (hdata->hdmiphy_port)
1880 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001881
Arvind Yadavd7420002016-10-19 15:34:16 +05301882 if (hdata->regs_hdmiphy)
1883 iounmap(hdata->regs_hdmiphy);
1884
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001885 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001886
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001887 return 0;
1888}
1889
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001890#ifdef CONFIG_PM
1891static int exynos_hdmi_suspend(struct device *dev)
1892{
1893 struct hdmi_context *hdata = dev_get_drvdata(dev);
1894
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001895 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001896
1897 return 0;
1898}
1899
1900static int exynos_hdmi_resume(struct device *dev)
1901{
1902 struct hdmi_context *hdata = dev_get_drvdata(dev);
1903 int ret;
1904
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001905 ret = hdmi_clk_enable_gates(hdata);
1906 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001907 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001908
1909 return 0;
1910}
1911#endif
1912
1913static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1914 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1915};
1916
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001917struct platform_driver hdmi_driver = {
1918 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001919 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001920 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301921 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001922 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001923 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301924 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001925 },
1926};