blob: 223b872f461b4887e740edcace25d186811d764e [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);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100800 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
801 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100802 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530803
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100804 ret = hdmi_audio_infoframe_init(&frm.audio);
805 if (!ret) {
806 frm.audio.channels = 2;
807 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
808 }
809 if (ret > 0) {
810 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
811 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530812 }
813}
814
Sean Pauld9716ee2014-01-30 16:19:29 -0500815static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
816 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500817{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200818 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500819
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200820 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200821 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530822
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200823 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500824}
825
Sean Pauld9716ee2014-01-30 16:19:29 -0500826static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900827{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200828 drm_connector_unregister(connector);
829 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900830}
831
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100832static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300833 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500834 .fill_modes = drm_helper_probe_single_connector_modes,
835 .detect = hdmi_detect,
836 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300837 .reset = drm_atomic_helper_connector_reset,
838 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
839 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500840};
841
842static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900843{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200844 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500845 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200846 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900847
Inki Dae8fa04aa2014-03-13 16:38:31 +0900848 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500849 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900850
Inki Dae8fa04aa2014-03-13 16:38:31 +0900851 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500852 if (!edid)
853 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900854
Sean Pauld9716ee2014-01-30 16:19:29 -0500855 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500856 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
857 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500858 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500859
Sean Pauld9716ee2014-01-30 16:19:29 -0500860 drm_mode_connector_update_edid_property(connector, edid);
861
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200862 ret = drm_add_edid_modes(connector, edid);
863
864 kfree(edid);
865
866 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900867}
868
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900869static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100871 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900872 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900873
Andrzej Hajda65e98032015-11-02 14:16:41 +0100874 for (i = 0; i < confs->count; i++)
875 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500876 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500877
878 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
879 return -EINVAL;
880}
881
Sean Pauld9716ee2014-01-30 16:19:29 -0500882static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500883 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900884{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200885 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900886 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900887
Rahul Sharma16844fb2013-06-10 14:50:00 +0530888 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
889 mode->hdisplay, mode->vdisplay, mode->vrefresh,
890 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
891 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900892
Rahul Sharma16844fb2013-06-10 14:50:00 +0530893 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900894 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500895 return MODE_BAD;
896
897 return MODE_OK;
898}
899
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100900static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500901 .get_modes = hdmi_get_modes,
902 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500903};
904
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300905static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500906{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300907 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500908 struct drm_connector *connector = &hdata->connector;
909 int ret;
910
Sean Pauld9716ee2014-01-30 16:19:29 -0500911 connector->interlace_allowed = true;
912 connector->polled = DRM_CONNECTOR_POLL_HPD;
913
914 ret = drm_connector_init(hdata->drm_dev, connector,
915 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
916 if (ret) {
917 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900918 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500919 }
920
921 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100922 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300923 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500924
925 return 0;
926}
927
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300928static bool hdmi_mode_fixup(struct drm_encoder *encoder,
929 const struct drm_display_mode *mode,
930 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500931{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300932 struct drm_device *dev = encoder->dev;
933 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500934 struct drm_display_mode *m;
935 int mode_ok;
936
Sean Paulf041b252014-01-30 16:19:15 -0500937 drm_mode_set_crtcinfo(adjusted_mode, 0);
938
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300939 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
940 if (connector->encoder == encoder)
941 break;
942 }
943
944 if (connector->encoder != encoder)
945 return true;
946
Sean Pauld9716ee2014-01-30 16:19:29 -0500947 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500948
Sean Pauld9716ee2014-01-30 16:19:29 -0500949 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300950 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500951
952 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900953 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500954 */
955 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500956 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500957
Sean Pauld9716ee2014-01-30 16:19:29 -0500958 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500959 DRM_INFO("desired mode doesn't exist so\n");
960 DRM_INFO("use the most suitable mode among modes.\n");
961
962 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
963 m->hdisplay, m->vdisplay, m->vrefresh);
964
Sean Paul75626852014-01-30 16:19:16 -0500965 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500966 break;
967 }
968 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300969
970 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500971}
972
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200973static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900974{
975 u32 n, cts;
976
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200977 cts = (freq % 9) ? 27000 : 30000;
978 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900979
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200980 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
981 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
982 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200983 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900984}
985
986static void hdmi_audio_init(struct hdmi_context *hdata)
987{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +0530988 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900989 u32 data_num, bit_ch, sample_frq;
990 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900991
992 sample_rate = 44100;
993 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900994
995 switch (bits_per_sample) {
996 case 20:
997 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900998 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900999 break;
1000 case 24:
1001 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001002 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001003 break;
1004 default:
1005 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001006 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001007 break;
1008 }
1009
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001010 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001011
1012 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1013 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1014 | HDMI_I2S_MUX_ENABLE);
1015
1016 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1017 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1018
1019 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1020
1021 sample_frq = (sample_rate == 44100) ? 0 :
1022 (sample_rate == 48000) ? 2 :
1023 (sample_rate == 32000) ? 3 :
1024 (sample_rate == 96000) ? 0xa : 0x0;
1025
1026 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1027 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1028
1029 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1030 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1031
1032 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1033 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1034 | HDMI_I2S_SEL_LRCK(6));
1035 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1036 | HDMI_I2S_SEL_SDATA2(4));
1037 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1038 | HDMI_I2S_SEL_SDATA2(2));
1039 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1040
1041 /* I2S_CON_1 & 2 */
1042 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1043 | HDMI_I2S_L_CH_LOW_POL);
1044 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1045 | HDMI_I2S_SET_BIT_CH(bit_ch)
1046 | HDMI_I2S_SET_SDATA_BIT(data_num)
1047 | HDMI_I2S_BASIC_FORMAT);
1048
1049 /* Configure register related to CUV information */
1050 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1051 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1052 | HDMI_I2S_COPYRIGHT
1053 | HDMI_I2S_LINEAR_PCM
1054 | HDMI_I2S_CONSUMER_FORMAT);
1055 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1056 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1057 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1058 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1059 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1060 HDMI_I2S_ORG_SMP_FREQ_44_1
1061 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1062 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1063
1064 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1065}
1066
1067static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1068{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001069 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001070 return;
1071
1072 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1073 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1074 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1075}
1076
Rahul Sharmabfa48422014-04-03 20:41:04 +05301077static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001078{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301079 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001080
Rahul Sharmabfa48422014-04-03 20:41:04 +05301081 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1082 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001083
Rahul Sharmabfa48422014-04-03 20:41:04 +05301084 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1085 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001086}
1087
1088static void hdmi_conf_init(struct hdmi_context *hdata)
1089{
Sean Paul77006a72013-01-16 10:17:20 -05001090 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001091 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1092 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001093
1094 /* choose HDMI mode */
1095 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1096 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001097 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301098 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001099 /* disable bluescreen */
1100 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001101
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001102 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001103 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1104 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1105 hdmi_reg_writeb(hdata, HDMI_CON_2,
1106 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1107 }
1108
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001109 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001110 /* choose bluescreen (fecal) color */
1111 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1112 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1113 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1114
1115 /* enable AVI packet every vsync, fixes purple line problem */
1116 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1117 /* force RGB, look to CEA-861-D, table 7 for more detail */
1118 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1119 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1120
1121 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1122 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1123 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1124 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001125 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301126
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001127 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001128 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1129 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001130}
1131
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001132static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1133{
1134 int tries;
1135
1136 for (tries = 0; tries < 10; ++tries) {
1137 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1138
1139 if (val & HDMI_PHY_STATUS_READY) {
1140 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1141 return;
1142 }
1143 usleep_range(10, 20);
1144 }
1145
1146 DRM_ERROR("PLL could not reach steady state\n");
1147}
1148
Rahul Sharma16844fb2013-06-10 14:50:00 +05301149static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001150{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001151 struct drm_display_mode *m = &hdata->current_mode;
1152 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001153
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001154 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1155 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1156 (m->htotal << 12) | m->vtotal);
1157
1158 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1159 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1160
1161 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1162 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1163
1164 val = (m->hsync_start - m->hdisplay - 2);
1165 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001166 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001167 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1168
1169 /*
1170 * Quirk requirement for exynos HDMI IP design,
1171 * 2 pixels less than the actual calculation for hsync_start
1172 * and end.
1173 */
1174
1175 /* Following values & calculations differ for different type of modes */
1176 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001177 val = ((m->vsync_end - m->vdisplay) / 2);
1178 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1179 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1180
1181 val = m->vtotal / 2;
1182 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1183 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1184
1185 val = (m->vtotal +
1186 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1187 val |= m->vtotal << 11;
1188 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1189
1190 val = ((m->vtotal / 2) + 7);
1191 val |= ((m->vtotal / 2) + 2) << 12;
1192 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1193
1194 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1195 val |= ((m->htotal / 2) +
1196 (m->hsync_start - m->hdisplay)) << 12;
1197 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1198
1199 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1200 (m->vtotal - m->vdisplay) / 2);
1201 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1202
1203 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1204 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001205 val = m->vtotal;
1206 val |= (m->vtotal - m->vdisplay) << 11;
1207 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1208
1209 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1210
1211 val = (m->vsync_end - m->vdisplay);
1212 val |= ((m->vsync_start - m->vdisplay) << 12);
1213 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1214
1215 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1216 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1217 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1218 m->vtotal - m->vdisplay);
1219 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001220 }
1221
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001222 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1223 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1224 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1225 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001226}
1227
Rahul Sharma16844fb2013-06-10 14:50:00 +05301228static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001229{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001230 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001231
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001232 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1233 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1234 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1235 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001236 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001237 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1238 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1239 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1240 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1241
1242 /*
1243 * Quirk requirement for exynos 5 HDMI IP design,
1244 * 2 pixels less than the actual calculation for hsync_start
1245 * and end.
1246 */
1247
1248 /* Following values & calculations differ for different type of modes */
1249 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001250 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1251 (m->vsync_end - m->vdisplay) / 2);
1252 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1253 (m->vsync_start - m->vdisplay) / 2);
1254 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1255 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1256 (m->vtotal - m->vdisplay) / 2);
1257 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1258 m->vtotal - m->vdisplay / 2);
1259 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1260 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1261 (m->vtotal / 2) + 7);
1262 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1263 (m->vtotal / 2) + 2);
1264 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1265 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1266 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1267 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1268 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1269 (m->vtotal - m->vdisplay) / 2);
1270 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1271 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1272 m->vtotal - m->vdisplay / 2);
1273 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1274 (m->vtotal / 2) + 1);
1275 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1276 (m->vtotal / 2) + 1);
1277 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1278 (m->vtotal / 2) + 1);
1279 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1280 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1281 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001282 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1283 m->vsync_end - m->vdisplay);
1284 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1285 m->vsync_start - m->vdisplay);
1286 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1287 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1288 m->vtotal - m->vdisplay);
1289 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1290 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1291 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1292 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1293 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1294 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1295 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1296 m->vtotal - m->vdisplay);
1297 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001298 }
1299
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001300 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1301 m->hsync_start - m->hdisplay - 2);
1302 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1303 m->hsync_end - m->hdisplay - 2);
1304 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1306 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1307 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1308 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1309 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1310 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1311 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1312 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1313 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1314 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1315 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1316 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1317 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1318 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1319 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001322
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001323 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1324 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1325 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1326 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001327 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1328 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001329}
1330
Rahul Sharma16844fb2013-06-10 14:50:00 +05301331static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001332{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001333 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301334 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001335 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301336 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001337
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001338 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001339}
1340
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001341static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1342{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001343 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1344 usleep_range(10000, 12000);
1345 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1346 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001347 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001348 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001349 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001350 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001351}
1352
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001353static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1354{
1355 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1356
1357 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1358 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1359}
1360
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001361static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1362{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001363 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001364 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001365
Andrzej Hajda4677f512016-03-23 14:15:12 +01001366 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1367 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001368 DRM_ERROR("failed to find hdmiphy conf\n");
1369 return;
1370 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001371 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1372
1373 hdmi_clk_set_parents(hdata, false);
1374
1375 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001376
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001377 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001378 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001379 if (ret) {
1380 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001381 return;
1382 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001383 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001384 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001385 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001386 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001387}
1388
1389static void hdmi_conf_apply(struct hdmi_context *hdata)
1390{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301391 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001392 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001393 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301394 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001395 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001396}
1397
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001398static void hdmi_mode_set(struct drm_encoder *encoder,
1399 struct drm_display_mode *mode,
1400 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001401{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001402 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001403 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001404
YoungJun Chocbc4c332013-06-12 10:44:40 +09001405 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1406 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001407 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001408 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001409
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001410 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001411}
1412
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001413static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1414{
1415 if (!hdata->sysreg)
1416 return;
1417
1418 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1419 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1420}
1421
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001422static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001423{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001424 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001425 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001426
Sean Paulaf65c802014-01-30 16:19:27 -05001427 pm_runtime_get_sync(hdata->dev);
1428
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001429 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001430 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1431
Rahul Sharma049d34e2014-05-20 10:36:05 +05301432 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1433 PMU_HDMI_PHY_ENABLE_BIT, 1);
1434
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001435 hdmi_set_refclk(hdata, true);
1436
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001437 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1438
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001439 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001440
1441 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001442}
1443
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001444static void hdmiphy_disable(struct hdmi_context *hdata)
1445{
1446 if (!hdata->powered)
1447 return;
1448
1449 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1450
1451 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1452
1453 hdmi_set_refclk(hdata, false);
1454
1455 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1456 PMU_HDMI_PHY_ENABLE_BIT, 0);
1457
1458 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1459
1460 pm_runtime_put_sync(hdata->dev);
1461
1462 hdata->powered = false;
1463}
1464
1465static void hdmi_enable(struct drm_encoder *encoder)
1466{
1467 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1468
1469 hdmiphy_enable(hdata);
1470 hdmi_conf_apply(hdata);
1471}
1472
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001473static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001474{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001475 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001476 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001477 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001478
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001479 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001480 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001481
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001482 /*
1483 * The SFRs of VP and Mixer are updated by Vertical Sync of
1484 * Timing generator which is a part of HDMI so the sequence
1485 * to disable TV Subsystem should be as following,
1486 * VP -> Mixer -> HDMI
1487 *
1488 * Below codes will try to disable Mixer and VP(if used)
1489 * prior to disabling HDMI.
1490 */
1491 if (crtc)
1492 funcs = crtc->helper_private;
1493 if (funcs && funcs->disable)
1494 (*funcs->disable)(crtc);
1495
Sean Paul724fd142014-05-09 15:05:10 +09001496 cancel_delayed_work(&hdata->hotplug_work);
1497
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001498 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001499}
1500
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001501static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001502 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001503 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001504 .enable = hdmi_enable,
1505 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001506};
1507
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001508static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001509 .destroy = drm_encoder_cleanup,
1510};
1511
Sean Paul724fd142014-05-09 15:05:10 +09001512static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001513{
Sean Paul724fd142014-05-09 15:05:10 +09001514 struct hdmi_context *hdata;
1515
1516 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001517
Sean Paul45517892014-01-30 16:19:05 -05001518 if (hdata->drm_dev)
1519 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001520}
1521
1522static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1523{
1524 struct hdmi_context *hdata = arg;
1525
1526 mod_delayed_work(system_wq, &hdata->hotplug_work,
1527 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001528
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001529 return IRQ_HANDLED;
1530}
1531
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001532static int hdmi_clks_get(struct hdmi_context *hdata,
1533 const struct string_array_spec *names,
1534 struct clk **clks)
1535{
1536 struct device *dev = hdata->dev;
1537 int i;
1538
1539 for (i = 0; i < names->count; ++i) {
1540 struct clk *clk = devm_clk_get(dev, names->data[i]);
1541
1542 if (IS_ERR(clk)) {
1543 int ret = PTR_ERR(clk);
1544
1545 dev_err(dev, "Cannot get clock %s, %d\n",
1546 names->data[i], ret);
1547
1548 return ret;
1549 }
1550
1551 clks[i] = clk;
1552 }
1553
1554 return 0;
1555}
1556
1557static int hdmi_clk_init(struct hdmi_context *hdata)
1558{
1559 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1560 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1561 struct device *dev = hdata->dev;
1562 struct clk **clks;
1563 int ret;
1564
1565 if (!count)
1566 return 0;
1567
1568 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1569 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001570 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001571
1572 hdata->clk_gates = clks;
1573 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1574
1575 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1576 if (ret)
1577 return ret;
1578
1579 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1580}
1581
1582
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001583static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1584{
1585 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1586 phy_clk);
1587
1588 if (enable)
1589 hdmiphy_enable(hdata);
1590 else
1591 hdmiphy_disable(hdata);
1592}
1593
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001594static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001595{
1596 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001597 int i, ret;
1598
1599 DRM_DEBUG_KMS("HDMI resource init\n");
1600
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001601 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1602 if (IS_ERR(hdata->hpd_gpio)) {
1603 DRM_ERROR("cannot get hpd gpio property\n");
1604 return PTR_ERR(hdata->hpd_gpio);
1605 }
1606
1607 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1608 if (hdata->irq < 0) {
1609 DRM_ERROR("failed to get GPIO irq\n");
1610 return hdata->irq;
1611 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001612
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001613 ret = hdmi_clk_init(hdata);
1614 if (ret)
1615 return ret;
1616
1617 ret = hdmi_clk_set_parents(hdata, false);
1618 if (ret)
1619 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001620
Milo Kimc0d656d2016-08-31 15:14:27 +09001621 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001622 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001623
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001624 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001625 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001626 if (ret != -EPROBE_DEFER)
1627 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001628 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001629 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001630
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001631 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001632
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001633 if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001634 return 0;
1635
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001636 if (IS_ERR(hdata->reg_hdmi_en))
1637 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001638
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001639 ret = regulator_enable(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001640 if (ret)
1641 DRM_ERROR("failed to enable hdmi-en regulator\n");
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001642
Inki Daedf5225b2014-05-29 18:28:02 +09001643 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001644}
1645
Rahul Sharma22c4f422012-10-04 20:48:55 +05301646static struct of_device_id hdmi_match_types[] = {
1647 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001648 .compatible = "samsung,exynos4210-hdmi",
1649 .data = &exynos4210_hdmi_driver_data,
1650 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301651 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001652 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301653 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301654 .compatible = "samsung,exynos5420-hdmi",
1655 .data = &exynos5420_hdmi_driver_data,
1656 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001657 .compatible = "samsung,exynos5433-hdmi",
1658 .data = &exynos5433_hdmi_driver_data,
1659 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301660 /* end node */
1661 }
1662};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001663MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301664
Inki Daef37cd5e2014-05-09 14:25:20 +09001665static int hdmi_bind(struct device *dev, struct device *master, void *data)
1666{
1667 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001668 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001669 struct drm_encoder *encoder = &hdata->encoder;
1670 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001671
Inki Daef37cd5e2014-05-09 14:25:20 +09001672 hdata->drm_dev = drm_dev;
1673
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001674 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1675 EXYNOS_DISPLAY_TYPE_HDMI);
1676 if (pipe < 0)
1677 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001678
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001679 hdata->phy_clk.enable = hdmiphy_clk_enable;
1680
1681 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1682
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001683 encoder->possible_crtcs = 1 << pipe;
1684
1685 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1686
1687 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001688 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001689
1690 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1691
1692 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001693 if (ret) {
1694 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001695 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001696 return ret;
1697 }
1698
1699 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001700}
1701
1702static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1703{
Inki Daef37cd5e2014-05-09 14:25:20 +09001704}
1705
1706static const struct component_ops hdmi_component_ops = {
1707 .bind = hdmi_bind,
1708 .unbind = hdmi_unbind,
1709};
1710
Milo Kim1caa3602016-08-31 15:14:25 +09001711static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001712{
1713 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1714 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001715 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001716
1717 np = of_find_compatible_node(NULL, NULL, compatible_str);
1718 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001719 np = of_get_next_parent(np);
1720 else
1721 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001722
Milo Kim1caa3602016-08-31 15:14:25 +09001723 if (!np) {
1724 DRM_ERROR("Failed to find ddc node in device tree\n");
1725 return -ENODEV;
1726 }
1727
1728 adpt = of_find_i2c_adapter_by_node(np);
1729 of_node_put(np);
1730
1731 if (!adpt) {
1732 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1733 return -EPROBE_DEFER;
1734 }
1735
1736 hdata->ddc_adpt = adpt;
1737
1738 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001739}
1740
Milo Kimb5413022016-08-31 15:14:26 +09001741static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001742{
1743 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001744 struct device_node *np;
1745 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001746
Milo Kimb5413022016-08-31 15:14:26 +09001747 np = of_find_compatible_node(NULL, NULL, compatible_str);
1748 if (!np) {
1749 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1750 if (!np) {
1751 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1752 return -ENODEV;
1753 }
1754 }
1755
1756 if (hdata->drv_data->is_apb_phy) {
1757 hdata->regs_hdmiphy = of_iomap(np, 0);
1758 if (!hdata->regs_hdmiphy) {
1759 DRM_ERROR("failed to ioremap hdmi phy\n");
1760 ret = -ENOMEM;
1761 goto out;
1762 }
1763 } else {
1764 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1765 if (!hdata->hdmiphy_port) {
1766 DRM_INFO("Failed to get hdmi phy i2c client\n");
1767 ret = -EPROBE_DEFER;
1768 goto out;
1769 }
1770 }
1771
1772out:
1773 of_node_put(np);
1774 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001775}
1776
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001777static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001778{
1779 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001780 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001781 struct resource *res;
1782 int ret;
1783
Andrzej Hajda930865f2014-11-17 09:54:20 +01001784 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1785 if (!hdata)
1786 return -ENOMEM;
1787
Marek Szyprowski57a64122016-04-01 15:17:44 +02001788 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001789
Andrzej Hajda930865f2014-11-17 09:54:20 +01001790 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001791
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001792 hdata->dev = dev;
1793
1794 ret = hdmi_resources_init(hdata);
1795 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001796 if (ret != -EPROBE_DEFER)
1797 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001798 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001799 }
1800
1801 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001802 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001803 if (IS_ERR(hdata->regs)) {
1804 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001805 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001806 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001807
Milo Kim1caa3602016-08-31 15:14:25 +09001808 ret = hdmi_get_ddc_adapter(hdata);
1809 if (ret)
1810 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001811
Milo Kimb5413022016-08-31 15:14:26 +09001812 ret = hdmi_get_phy_io(hdata);
1813 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001814 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001815
Sean Paul724fd142014-05-09 15:05:10 +09001816 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1817
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001818 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001819 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001820 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001821 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001822 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001823 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001824 goto err_hdmiphy;
1825 }
1826
Rahul Sharma049d34e2014-05-20 10:36:05 +05301827 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1828 "samsung,syscon-phandle");
1829 if (IS_ERR(hdata->pmureg)) {
1830 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001831 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301832 goto err_hdmiphy;
1833 }
1834
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001835 if (hdata->drv_data->has_sysreg) {
1836 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1837 "samsung,sysreg-phandle");
1838 if (IS_ERR(hdata->sysreg)) {
1839 DRM_ERROR("sysreg regmap lookup failed.\n");
1840 ret = -EPROBE_DEFER;
1841 goto err_hdmiphy;
1842 }
1843 }
1844
Sean Paulaf65c802014-01-30 16:19:27 -05001845 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001846
Inki Daedf5225b2014-05-29 18:28:02 +09001847 ret = component_add(&pdev->dev, &hdmi_component_ops);
1848 if (ret)
1849 goto err_disable_pm_runtime;
1850
1851 return ret;
1852
1853err_disable_pm_runtime:
1854 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001855
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001856err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001857 if (hdata->hdmiphy_port)
1858 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301859 if (hdata->regs_hdmiphy)
1860 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001861err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001862 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001863
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001864 return ret;
1865}
1866
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001867static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001868{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001869 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001870
Sean Paul724fd142014-05-09 15:05:10 +09001871 cancel_delayed_work_sync(&hdata->hotplug_work);
1872
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001873 component_del(&pdev->dev, &hdmi_component_ops);
1874
1875 pm_runtime_disable(&pdev->dev);
1876
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001877 if (!IS_ERR(hdata->reg_hdmi_en))
1878 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001879
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001880 if (hdata->hdmiphy_port)
1881 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001882
Arvind Yadavd7420002016-10-19 15:34:16 +05301883 if (hdata->regs_hdmiphy)
1884 iounmap(hdata->regs_hdmiphy);
1885
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001886 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001887
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001888 return 0;
1889}
1890
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001891#ifdef CONFIG_PM
1892static int exynos_hdmi_suspend(struct device *dev)
1893{
1894 struct hdmi_context *hdata = dev_get_drvdata(dev);
1895
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001896 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001897
1898 return 0;
1899}
1900
1901static int exynos_hdmi_resume(struct device *dev)
1902{
1903 struct hdmi_context *hdata = dev_get_drvdata(dev);
1904 int ret;
1905
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001906 ret = hdmi_clk_enable_gates(hdata);
1907 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001908 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001909
1910 return 0;
1911}
1912#endif
1913
1914static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1915 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1916};
1917
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001918struct platform_driver hdmi_driver = {
1919 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001920 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001921 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301922 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001923 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001924 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301925 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001926 },
1927};