blob: bc4c8d0a66f41748964d2ab95244d0d472d94d71 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090010 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
Seung-Woo Kimd8408322011-12-21 17:39:39 +090013 * option) any later version.
14 *
15 */
16
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
18#include <drm/drm_edid.h>
19#include <drm/drm_crtc_helper.h>
Gustavo Padovan4ea95262015-06-01 12:04:44 -030020#include <drm/drm_atomic_helper.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090021
22#include "regs-hdmi.h"
23
24#include <linux/kernel.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090025#include <linux/wait.h>
26#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090027#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
Andrzej Hajda2228b7c2015-09-25 14:48:24 +020033#include <linux/gpio/consumer.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090034#include <linux/regulator/consumer.h>
Rahul Sharma22c4f422012-10-04 20:48:55 +053035#include <linux/io.h>
Rahul Sharmad5e9ca42014-05-09 15:34:18 +090036#include <linux/of_address.h>
Andrzej Hajdacd240cd2015-07-09 16:28:09 +020037#include <linux/of_device.h>
Andrzej Hajdaaa181572017-02-01 09:29:14 +010038#include <linux/of_graph.h>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053039#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090040#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053041#include <linux/mfd/syscon.h>
42#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090043
44#include <drm/exynos_drm.h>
45
Hans Verkuil278c8112016-12-13 11:07:17 -020046#include <media/cec-notifier.h>
47
Seung-Woo Kimd8408322011-12-21 17:39:39 +090048#include "exynos_drm_drv.h"
Inki Daef37cd5e2014-05-09 14:25:20 +090049#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090050
Sean Paul724fd142014-05-09 15:05:10 +090051#define HOTPLUG_DEBOUNCE_MS 1100
52
Rahul Sharma5a325072012-10-04 20:48:54 +053053enum hdmi_type {
54 HDMI_TYPE13,
55 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020056 HDMI_TYPE_COUNT
57};
58
59#define HDMI_MAPPED_BASE 0xffff0000
60
61enum hdmi_mapped_regs {
62 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
63 HDMI_PHY_RSTOUT,
64 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020065 HDMI_ACR_MCTS0,
66 HDMI_ACR_CTS0,
67 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020068};
69
70static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
71 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
72 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
73 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020074 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
75 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
76 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053077};
78
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020079static const char * const supply[] = {
80 "vdd",
81 "vdd_osc",
82 "vdd_pll",
83};
84
Andrzej Hajda65e98032015-11-02 14:16:41 +010085struct hdmiphy_config {
86 int pixel_clock;
87 u8 conf[32];
88};
89
90struct hdmiphy_configs {
91 int count;
92 const struct hdmiphy_config *data;
93};
94
Andrzej Hajda9be7e982016-01-14 14:22:47 +090095struct string_array_spec {
96 int count;
97 const char * const *data;
98};
99
100#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
101
Inki Daebfe4e842014-03-06 14:18:17 +0900102struct hdmi_driver_data {
103 unsigned int type;
104 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900105 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100106 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900107 struct string_array_spec clk_gates;
108 /*
109 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
110 * required parents of clock when HDMI-PHY is respectively off or on.
111 */
112 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900113};
114
Joonyoung Shim590f4182012-03-16 18:47:14 +0900115struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300116 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900117 struct device *dev;
118 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500119 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900120 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900121 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900122 struct delayed_work hotplug_work;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530123 struct drm_display_mode current_mode;
Hans Verkuil278c8112016-12-13 11:07:17 -0200124 struct cec_notifier *notifier;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200125 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900126
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200127 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900128 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200129 struct i2c_client *hdmiphy_port;
130 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900131 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200132 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530133 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900134 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900135 struct clk **clk_gates;
136 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200137 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
138 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900139 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100140 struct drm_bridge *bridge;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900141};
142
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300143static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100144{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900145 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100146}
147
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200148static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
149{
150 return container_of(c, struct hdmi_context, connector);
151}
152
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900153static const struct hdmiphy_config hdmiphy_v13_configs[] = {
154 {
155 .pixel_clock = 27000000,
156 .conf = {
157 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
158 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
159 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200160 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900161 },
162 },
163 {
164 .pixel_clock = 27027000,
165 .conf = {
166 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
167 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
168 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200169 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900170 },
171 },
172 {
173 .pixel_clock = 74176000,
174 .conf = {
175 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
176 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
177 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200178 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900179 },
180 },
181 {
182 .pixel_clock = 74250000,
183 .conf = {
184 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
185 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
186 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200187 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900188 },
189 },
190 {
191 .pixel_clock = 148500000,
192 .conf = {
193 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
194 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
195 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200196 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900197 },
198 },
199};
200
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500201static const struct hdmiphy_config hdmiphy_v14_configs[] = {
202 {
203 .pixel_clock = 25200000,
204 .conf = {
205 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
206 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
207 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
208 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
209 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900210 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500211 {
212 .pixel_clock = 27000000,
213 .conf = {
214 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
215 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
216 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
217 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
218 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900219 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500220 {
221 .pixel_clock = 27027000,
222 .conf = {
223 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
224 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
225 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200226 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500227 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900228 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500229 {
230 .pixel_clock = 36000000,
231 .conf = {
232 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
233 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
234 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
235 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
236 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900237 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500238 {
239 .pixel_clock = 40000000,
240 .conf = {
241 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
242 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
243 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
244 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
245 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900246 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500247 {
248 .pixel_clock = 65000000,
249 .conf = {
250 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
251 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
252 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
253 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
254 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900255 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500256 {
Shirish Se1d883c2014-03-13 14:28:27 +0900257 .pixel_clock = 71000000,
258 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530259 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
260 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
261 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900262 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
263 },
264 },
265 {
266 .pixel_clock = 73250000,
267 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530268 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
269 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
270 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900271 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
272 },
273 },
274 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500275 .pixel_clock = 74176000,
276 .conf = {
277 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
278 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
279 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
280 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
281 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900282 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500283 {
284 .pixel_clock = 74250000,
285 .conf = {
286 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
287 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
288 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200289 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500290 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900291 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500292 {
293 .pixel_clock = 83500000,
294 .conf = {
295 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
296 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
297 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
298 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
299 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900300 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500301 {
302 .pixel_clock = 106500000,
303 .conf = {
304 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
305 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
306 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
307 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
308 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900309 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500310 {
311 .pixel_clock = 108000000,
312 .conf = {
313 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
314 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
315 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
316 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
317 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900318 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500319 {
Shirish Se1d883c2014-03-13 14:28:27 +0900320 .pixel_clock = 115500000,
321 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530322 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
323 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
324 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900325 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
326 },
327 },
328 {
329 .pixel_clock = 119000000,
330 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530331 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
332 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
333 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900334 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
335 },
336 },
337 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500338 .pixel_clock = 146250000,
339 .conf = {
340 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
341 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
342 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
343 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
344 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900345 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500346 {
347 .pixel_clock = 148500000,
348 .conf = {
349 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
350 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
351 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200352 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500353 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900354 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900355};
356
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530357static const struct hdmiphy_config hdmiphy_5420_configs[] = {
358 {
359 .pixel_clock = 25200000,
360 .conf = {
361 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
362 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
363 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
364 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
365 },
366 },
367 {
368 .pixel_clock = 27000000,
369 .conf = {
370 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
371 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
372 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
373 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
374 },
375 },
376 {
377 .pixel_clock = 27027000,
378 .conf = {
379 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
380 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
381 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
382 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
383 },
384 },
385 {
386 .pixel_clock = 36000000,
387 .conf = {
388 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
389 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
390 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
391 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
392 },
393 },
394 {
395 .pixel_clock = 40000000,
396 .conf = {
397 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
398 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
399 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
400 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
401 },
402 },
403 {
404 .pixel_clock = 65000000,
405 .conf = {
406 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
407 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
408 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
409 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
410 },
411 },
412 {
413 .pixel_clock = 71000000,
414 .conf = {
415 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
416 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
417 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
418 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
419 },
420 },
421 {
422 .pixel_clock = 73250000,
423 .conf = {
424 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
425 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
426 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
427 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
428 },
429 },
430 {
431 .pixel_clock = 74176000,
432 .conf = {
433 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
434 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
435 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
436 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
437 },
438 },
439 {
440 .pixel_clock = 74250000,
441 .conf = {
442 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
443 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
444 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
445 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
446 },
447 },
448 {
449 .pixel_clock = 83500000,
450 .conf = {
451 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
452 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
453 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
454 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
455 },
456 },
457 {
458 .pixel_clock = 88750000,
459 .conf = {
460 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
461 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
462 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
463 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
464 },
465 },
466 {
467 .pixel_clock = 106500000,
468 .conf = {
469 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
470 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
471 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
472 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
473 },
474 },
475 {
476 .pixel_clock = 108000000,
477 .conf = {
478 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
479 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
480 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
481 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
482 },
483 },
484 {
485 .pixel_clock = 115500000,
486 .conf = {
487 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
488 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
489 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
490 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
491 },
492 },
493 {
494 .pixel_clock = 146250000,
495 .conf = {
496 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
497 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
498 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
499 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
500 },
501 },
502 {
503 .pixel_clock = 148500000,
504 .conf = {
505 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
506 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
507 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
508 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
509 },
510 },
511};
512
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900513static const struct hdmiphy_config hdmiphy_5433_configs[] = {
514 {
515 .pixel_clock = 27000000,
516 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100517 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
518 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
519 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900520 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
521 },
522 },
523 {
524 .pixel_clock = 27027000,
525 .conf = {
526 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100527 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
528 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
529 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900530 },
531 },
532 {
533 .pixel_clock = 40000000,
534 .conf = {
535 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
536 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
537 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
538 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
539 },
540 },
541 {
542 .pixel_clock = 50000000,
543 .conf = {
544 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
545 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
546 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
547 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
548 },
549 },
550 {
551 .pixel_clock = 65000000,
552 .conf = {
553 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
554 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
555 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
556 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
557 },
558 },
559 {
560 .pixel_clock = 74176000,
561 .conf = {
562 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
563 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
564 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
565 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
566 },
567 },
568 {
569 .pixel_clock = 74250000,
570 .conf = {
571 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
572 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
573 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
574 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
575 },
576 },
577 {
578 .pixel_clock = 108000000,
579 .conf = {
580 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
581 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
582 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
583 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
584 },
585 },
586 {
587 .pixel_clock = 148500000,
588 .conf = {
589 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
590 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
591 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
592 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
593 },
594 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100595 {
596 .pixel_clock = 297000000,
597 .conf = {
598 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
599 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
600 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
601 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
602 },
603 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900604};
605
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100606static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900607 "hdmi", "sclk_hdmi"
608};
609
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100610static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900611 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
612};
613
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900614static const char * const hdmi_clk_gates5433[] = {
615 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
616};
617
618static const char * const hdmi_clk_muxes5433[] = {
619 "oscclk", "tmds_clko", "tmds_clko_user",
620 "oscclk", "pixel_clko", "pixel_clko_user"
621};
622
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900623static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
624 .type = HDMI_TYPE13,
625 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900626 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
627 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530628};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900629
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100630static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900631 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100632 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900633 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
634 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900635};
636
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900637static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
638 .type = HDMI_TYPE14,
639 .is_apb_phy = 1,
640 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900641 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
642 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200643};
644
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900645static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
646 .type = HDMI_TYPE14,
647 .is_apb_phy = 1,
648 .has_sysreg = 1,
649 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
650 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
651 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
652};
653
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200654static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
655{
656 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
657 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
658 return reg_id;
659}
660
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900661static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
662{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200663 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900664}
665
666static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
667 u32 reg_id, u8 value)
668{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200669 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900670}
671
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200672static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
673 int bytes, u32 val)
674{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200675 reg_id = hdmi_map_reg(hdata, reg_id);
676
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200677 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200678 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200679 val >>= 8;
680 reg_id += 4;
681 }
682}
683
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100684static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
685 u8 *buf, int size)
686{
687 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
688 writel(*buf++, hdata->regs + reg_id);
689}
690
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900691static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
692 u32 reg_id, u32 value, u32 mask)
693{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200694 u32 old;
695
696 reg_id = hdmi_map_reg(hdata, reg_id);
697 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900698 value = (value & mask) | (old & ~mask);
699 writel(value, hdata->regs + reg_id);
700}
701
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900702static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
703 u32 reg_offset, const u8 *buf, u32 len)
704{
705 if ((reg_offset + len) > 32)
706 return -EINVAL;
707
708 if (hdata->hdmiphy_port) {
709 int ret;
710
711 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
712 if (ret == len)
713 return 0;
714 return ret;
715 } else {
716 int i;
717 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200718 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900719 ((reg_offset + i)<<2));
720 return 0;
721 }
722}
723
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900724static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
725{
726 int i, ret;
727
728 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
729 ret = clk_prepare_enable(hdata->clk_gates[i]);
730 if (!ret)
731 continue;
732
733 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
734 hdata->drv_data->clk_gates.data[i], ret);
735 while (i--)
736 clk_disable_unprepare(hdata->clk_gates[i]);
737 return ret;
738 }
739
740 return 0;
741}
742
743static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
744{
745 int i = hdata->drv_data->clk_gates.count;
746
747 while (i--)
748 clk_disable_unprepare(hdata->clk_gates[i]);
749}
750
751static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
752{
753 struct device *dev = hdata->dev;
754 int ret = 0;
755 int i;
756
757 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
758 struct clk **c = &hdata->clk_muxes[i];
759
760 ret = clk_set_parent(c[2], c[to_phy]);
761 if (!ret)
762 continue;
763
764 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
765 hdata->drv_data->clk_muxes.data[i + 2],
766 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
767 }
768
769 return ret;
770}
771
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100772static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530773{
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100774 union hdmi_infoframe frm;
775 u8 buf[25];
776 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530777
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530778 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530779 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
780 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100781 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
782 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530783 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
784 return;
785 }
786
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100787 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
788 &hdata->current_mode);
789 if (!ret)
790 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
791 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530792 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100793 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
794 } else {
795 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
796 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530797
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100798 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
799 &hdata->current_mode);
800 if (!ret)
801 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
802 sizeof(buf));
803 if (ret > 0) {
804 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100805 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
806 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100807 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530808
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100809 ret = hdmi_audio_infoframe_init(&frm.audio);
810 if (!ret) {
811 frm.audio.channels = 2;
812 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
813 }
814 if (ret > 0) {
815 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
816 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530817 }
818}
819
Sean Pauld9716ee2014-01-30 16:19:29 -0500820static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
821 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500822{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200823 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500824
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200825 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200826 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530827
Hans Verkuil278c8112016-12-13 11:07:17 -0200828 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200829 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500830}
831
Sean Pauld9716ee2014-01-30 16:19:29 -0500832static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900833{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200834 drm_connector_unregister(connector);
835 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900836}
837
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100838static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300839 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500840 .fill_modes = drm_helper_probe_single_connector_modes,
841 .detect = hdmi_detect,
842 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300843 .reset = drm_atomic_helper_connector_reset,
844 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
845 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500846};
847
848static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900849{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200850 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500851 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200852 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900853
Inki Dae8fa04aa2014-03-13 16:38:31 +0900854 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500855 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900856
Inki Dae8fa04aa2014-03-13 16:38:31 +0900857 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500858 if (!edid)
859 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900860
Sean Pauld9716ee2014-01-30 16:19:29 -0500861 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500862 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
863 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500864 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500865
Sean Pauld9716ee2014-01-30 16:19:29 -0500866 drm_mode_connector_update_edid_property(connector, edid);
Hans Verkuil278c8112016-12-13 11:07:17 -0200867 cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
Sean Pauld9716ee2014-01-30 16:19:29 -0500868
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200869 ret = drm_add_edid_modes(connector, edid);
870
871 kfree(edid);
872
873 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900874}
875
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900876static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900877{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100878 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900879 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900880
Andrzej Hajda65e98032015-11-02 14:16:41 +0100881 for (i = 0; i < confs->count; i++)
882 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500883 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500884
885 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
886 return -EINVAL;
887}
888
Sean Pauld9716ee2014-01-30 16:19:29 -0500889static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500890 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900891{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200892 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900893 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900894
Rahul Sharma16844fb2013-06-10 14:50:00 +0530895 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
896 mode->hdisplay, mode->vdisplay, mode->vrefresh,
897 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
898 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900899
Rahul Sharma16844fb2013-06-10 14:50:00 +0530900 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900901 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500902 return MODE_BAD;
903
904 return MODE_OK;
905}
906
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100907static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500908 .get_modes = hdmi_get_modes,
909 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500910};
911
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300912static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500913{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300914 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500915 struct drm_connector *connector = &hdata->connector;
916 int ret;
917
Sean Pauld9716ee2014-01-30 16:19:29 -0500918 connector->interlace_allowed = true;
919 connector->polled = DRM_CONNECTOR_POLL_HPD;
920
921 ret = drm_connector_init(hdata->drm_dev, connector,
922 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
923 if (ret) {
924 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900925 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500926 }
927
928 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100929 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300930 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500931
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100932 if (hdata->bridge) {
933 encoder->bridge = hdata->bridge;
934 hdata->bridge->encoder = encoder;
935 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
936 if (ret)
937 DRM_ERROR("Failed to attach bridge\n");
938 }
939
940 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500941}
942
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300943static bool hdmi_mode_fixup(struct drm_encoder *encoder,
944 const struct drm_display_mode *mode,
945 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500946{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300947 struct drm_device *dev = encoder->dev;
948 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500949 struct drm_display_mode *m;
950 int mode_ok;
951
Sean Paulf041b252014-01-30 16:19:15 -0500952 drm_mode_set_crtcinfo(adjusted_mode, 0);
953
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300954 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
955 if (connector->encoder == encoder)
956 break;
957 }
958
959 if (connector->encoder != encoder)
960 return true;
961
Sean Pauld9716ee2014-01-30 16:19:29 -0500962 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500963
Sean Pauld9716ee2014-01-30 16:19:29 -0500964 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300965 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500966
967 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900968 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500969 */
970 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500971 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500972
Sean Pauld9716ee2014-01-30 16:19:29 -0500973 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500974 DRM_INFO("desired mode doesn't exist so\n");
975 DRM_INFO("use the most suitable mode among modes.\n");
976
977 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
978 m->hdisplay, m->vdisplay, m->vrefresh);
979
Sean Paul75626852014-01-30 16:19:16 -0500980 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500981 break;
982 }
983 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300984
985 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500986}
987
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200988static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900989{
990 u32 n, cts;
991
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200992 cts = (freq % 9) ? 27000 : 30000;
993 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900994
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200995 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
996 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
997 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200998 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900999}
1000
1001static void hdmi_audio_init(struct hdmi_context *hdata)
1002{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301003 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001004 u32 data_num, bit_ch, sample_frq;
1005 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001006
1007 sample_rate = 44100;
1008 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001009
1010 switch (bits_per_sample) {
1011 case 20:
1012 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001013 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001014 break;
1015 case 24:
1016 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001017 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001018 break;
1019 default:
1020 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001021 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001022 break;
1023 }
1024
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001025 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001026
1027 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1028 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1029 | HDMI_I2S_MUX_ENABLE);
1030
1031 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1032 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1033
1034 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1035
1036 sample_frq = (sample_rate == 44100) ? 0 :
1037 (sample_rate == 48000) ? 2 :
1038 (sample_rate == 32000) ? 3 :
1039 (sample_rate == 96000) ? 0xa : 0x0;
1040
1041 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1042 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1043
1044 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1045 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1046
1047 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1048 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1049 | HDMI_I2S_SEL_LRCK(6));
1050 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1051 | HDMI_I2S_SEL_SDATA2(4));
1052 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1053 | HDMI_I2S_SEL_SDATA2(2));
1054 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1055
1056 /* I2S_CON_1 & 2 */
1057 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1058 | HDMI_I2S_L_CH_LOW_POL);
1059 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1060 | HDMI_I2S_SET_BIT_CH(bit_ch)
1061 | HDMI_I2S_SET_SDATA_BIT(data_num)
1062 | HDMI_I2S_BASIC_FORMAT);
1063
1064 /* Configure register related to CUV information */
1065 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1066 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1067 | HDMI_I2S_COPYRIGHT
1068 | HDMI_I2S_LINEAR_PCM
1069 | HDMI_I2S_CONSUMER_FORMAT);
1070 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1071 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1072 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1073 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1074 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1075 HDMI_I2S_ORG_SMP_FREQ_44_1
1076 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1077 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1078
1079 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1080}
1081
1082static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1083{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001084 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001085 return;
1086
1087 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1088 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1089 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1090}
1091
Rahul Sharmabfa48422014-04-03 20:41:04 +05301092static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001093{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301094 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001095
Rahul Sharmabfa48422014-04-03 20:41:04 +05301096 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1097 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001098
Rahul Sharmabfa48422014-04-03 20:41:04 +05301099 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1100 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001101}
1102
1103static void hdmi_conf_init(struct hdmi_context *hdata)
1104{
Sean Paul77006a72013-01-16 10:17:20 -05001105 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001106 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1107 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001108
1109 /* choose HDMI mode */
1110 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1111 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001112 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301113 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001114 /* disable bluescreen */
1115 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001116
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001117 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001118 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1119 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1120 hdmi_reg_writeb(hdata, HDMI_CON_2,
1121 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1122 }
1123
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001124 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001125 /* choose bluescreen (fecal) color */
1126 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1127 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1128 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1129
1130 /* enable AVI packet every vsync, fixes purple line problem */
1131 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1132 /* force RGB, look to CEA-861-D, table 7 for more detail */
1133 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1134 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1135
1136 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1137 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1138 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1139 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001140 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301141
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001142 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001143 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1144 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001145}
1146
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001147static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1148{
1149 int tries;
1150
1151 for (tries = 0; tries < 10; ++tries) {
1152 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1153
1154 if (val & HDMI_PHY_STATUS_READY) {
1155 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1156 return;
1157 }
1158 usleep_range(10, 20);
1159 }
1160
1161 DRM_ERROR("PLL could not reach steady state\n");
1162}
1163
Rahul Sharma16844fb2013-06-10 14:50:00 +05301164static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001165{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001166 struct drm_display_mode *m = &hdata->current_mode;
1167 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001168
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001169 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1170 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1171 (m->htotal << 12) | m->vtotal);
1172
1173 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1174 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1175
1176 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1177 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1178
1179 val = (m->hsync_start - m->hdisplay - 2);
1180 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001181 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001182 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1183
1184 /*
1185 * Quirk requirement for exynos HDMI IP design,
1186 * 2 pixels less than the actual calculation for hsync_start
1187 * and end.
1188 */
1189
1190 /* Following values & calculations differ for different type of modes */
1191 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001192 val = ((m->vsync_end - m->vdisplay) / 2);
1193 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1194 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1195
1196 val = m->vtotal / 2;
1197 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1198 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1199
1200 val = (m->vtotal +
1201 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1202 val |= m->vtotal << 11;
1203 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1204
1205 val = ((m->vtotal / 2) + 7);
1206 val |= ((m->vtotal / 2) + 2) << 12;
1207 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1208
1209 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1210 val |= ((m->htotal / 2) +
1211 (m->hsync_start - m->hdisplay)) << 12;
1212 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1213
1214 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1215 (m->vtotal - m->vdisplay) / 2);
1216 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1217
1218 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1219 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001220 val = m->vtotal;
1221 val |= (m->vtotal - m->vdisplay) << 11;
1222 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1223
1224 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1225
1226 val = (m->vsync_end - m->vdisplay);
1227 val |= ((m->vsync_start - m->vdisplay) << 12);
1228 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1229
1230 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1231 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1232 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1233 m->vtotal - m->vdisplay);
1234 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001235 }
1236
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001237 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1238 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1239 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1240 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001241}
1242
Rahul Sharma16844fb2013-06-10 14:50:00 +05301243static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001244{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001245 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001246
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001247 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1248 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1249 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1250 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001251 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001252 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1253 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1254 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1255 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1256
1257 /*
1258 * Quirk requirement for exynos 5 HDMI IP design,
1259 * 2 pixels less than the actual calculation for hsync_start
1260 * and end.
1261 */
1262
1263 /* Following values & calculations differ for different type of modes */
1264 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001265 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1266 (m->vsync_end - m->vdisplay) / 2);
1267 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1268 (m->vsync_start - m->vdisplay) / 2);
1269 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1270 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1271 (m->vtotal - m->vdisplay) / 2);
1272 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1273 m->vtotal - m->vdisplay / 2);
1274 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1275 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1276 (m->vtotal / 2) + 7);
1277 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1278 (m->vtotal / 2) + 2);
1279 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1280 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1281 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1282 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1283 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1284 (m->vtotal - m->vdisplay) / 2);
1285 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1286 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1287 m->vtotal - m->vdisplay / 2);
1288 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1289 (m->vtotal / 2) + 1);
1290 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1291 (m->vtotal / 2) + 1);
1292 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1293 (m->vtotal / 2) + 1);
1294 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1295 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1296 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001297 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1298 m->vsync_end - m->vdisplay);
1299 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1300 m->vsync_start - m->vdisplay);
1301 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1302 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1303 m->vtotal - m->vdisplay);
1304 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1306 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1307 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1308 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1309 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1310 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1311 m->vtotal - m->vdisplay);
1312 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001313 }
1314
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001315 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1316 m->hsync_start - m->hdisplay - 2);
1317 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1318 m->hsync_end - m->hdisplay - 2);
1319 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1322 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1323 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1327 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1328 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1329 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1330 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1331 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1332 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1333 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1334 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1335 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1336 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001337
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001338 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1339 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1340 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1341 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001342 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1343 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001344}
1345
Rahul Sharma16844fb2013-06-10 14:50:00 +05301346static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001347{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001348 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301349 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001350 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301351 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001352
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001353 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001354}
1355
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001356static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1357{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001358 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1359 usleep_range(10000, 12000);
1360 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1361 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001362 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001363 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001364 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001365 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001366}
1367
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001368static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1369{
1370 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1371
1372 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1373 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1374}
1375
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001376static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1377{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001378 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001379 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001380
Andrzej Hajda4677f512016-03-23 14:15:12 +01001381 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1382 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001383 DRM_ERROR("failed to find hdmiphy conf\n");
1384 return;
1385 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001386 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1387
1388 hdmi_clk_set_parents(hdata, false);
1389
1390 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001391
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001392 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001393 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001394 if (ret) {
1395 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001396 return;
1397 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001398 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001399 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001400 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001401 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001402}
1403
1404static void hdmi_conf_apply(struct hdmi_context *hdata)
1405{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301406 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001407 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001408 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301409 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001410 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001411}
1412
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001413static void hdmi_mode_set(struct drm_encoder *encoder,
1414 struct drm_display_mode *mode,
1415 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001416{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001417 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001418 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001419
YoungJun Chocbc4c332013-06-12 10:44:40 +09001420 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1421 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001422 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001423 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001424
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001425 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001426}
1427
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001428static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1429{
1430 if (!hdata->sysreg)
1431 return;
1432
1433 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1434 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1435}
1436
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001437static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001438{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001439 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001440 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001441
Sean Paulaf65c802014-01-30 16:19:27 -05001442 pm_runtime_get_sync(hdata->dev);
1443
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001444 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001445 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1446
Rahul Sharma049d34e2014-05-20 10:36:05 +05301447 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1448 PMU_HDMI_PHY_ENABLE_BIT, 1);
1449
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001450 hdmi_set_refclk(hdata, true);
1451
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001452 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1453
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001454 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001455
1456 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001457}
1458
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001459static void hdmiphy_disable(struct hdmi_context *hdata)
1460{
1461 if (!hdata->powered)
1462 return;
1463
1464 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1465
1466 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1467
1468 hdmi_set_refclk(hdata, false);
1469
1470 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1471 PMU_HDMI_PHY_ENABLE_BIT, 0);
1472
1473 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1474
1475 pm_runtime_put_sync(hdata->dev);
1476
1477 hdata->powered = false;
1478}
1479
1480static void hdmi_enable(struct drm_encoder *encoder)
1481{
1482 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1483
1484 hdmiphy_enable(hdata);
1485 hdmi_conf_apply(hdata);
1486}
1487
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001488static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001489{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001490 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001491 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001492 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001493
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001494 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001495 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001496
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001497 /*
1498 * The SFRs of VP and Mixer are updated by Vertical Sync of
1499 * Timing generator which is a part of HDMI so the sequence
1500 * to disable TV Subsystem should be as following,
1501 * VP -> Mixer -> HDMI
1502 *
1503 * Below codes will try to disable Mixer and VP(if used)
1504 * prior to disabling HDMI.
1505 */
1506 if (crtc)
1507 funcs = crtc->helper_private;
1508 if (funcs && funcs->disable)
1509 (*funcs->disable)(crtc);
1510
Hans Verkuil278c8112016-12-13 11:07:17 -02001511 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09001512 cancel_delayed_work(&hdata->hotplug_work);
1513
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001514 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001515}
1516
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001517static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001518 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001519 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001520 .enable = hdmi_enable,
1521 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001522};
1523
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001524static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001525 .destroy = drm_encoder_cleanup,
1526};
1527
Sean Paul724fd142014-05-09 15:05:10 +09001528static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001529{
Sean Paul724fd142014-05-09 15:05:10 +09001530 struct hdmi_context *hdata;
1531
1532 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001533
Sean Paul45517892014-01-30 16:19:05 -05001534 if (hdata->drm_dev)
1535 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001536}
1537
1538static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1539{
1540 struct hdmi_context *hdata = arg;
1541
1542 mod_delayed_work(system_wq, &hdata->hotplug_work,
1543 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001544
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001545 return IRQ_HANDLED;
1546}
1547
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001548static int hdmi_clks_get(struct hdmi_context *hdata,
1549 const struct string_array_spec *names,
1550 struct clk **clks)
1551{
1552 struct device *dev = hdata->dev;
1553 int i;
1554
1555 for (i = 0; i < names->count; ++i) {
1556 struct clk *clk = devm_clk_get(dev, names->data[i]);
1557
1558 if (IS_ERR(clk)) {
1559 int ret = PTR_ERR(clk);
1560
1561 dev_err(dev, "Cannot get clock %s, %d\n",
1562 names->data[i], ret);
1563
1564 return ret;
1565 }
1566
1567 clks[i] = clk;
1568 }
1569
1570 return 0;
1571}
1572
1573static int hdmi_clk_init(struct hdmi_context *hdata)
1574{
1575 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1576 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1577 struct device *dev = hdata->dev;
1578 struct clk **clks;
1579 int ret;
1580
1581 if (!count)
1582 return 0;
1583
1584 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1585 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001586 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001587
1588 hdata->clk_gates = clks;
1589 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1590
1591 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1592 if (ret)
1593 return ret;
1594
1595 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1596}
1597
1598
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001599static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1600{
1601 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1602 phy_clk);
1603
1604 if (enable)
1605 hdmiphy_enable(hdata);
1606 else
1607 hdmiphy_disable(hdata);
1608}
1609
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001610static int hdmi_bridge_init(struct hdmi_context *hdata)
1611{
1612 struct device *dev = hdata->dev;
1613 struct device_node *ep, *np;
1614
1615 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1616 if (!ep)
1617 return 0;
1618
1619 np = of_graph_get_remote_port_parent(ep);
1620 of_node_put(ep);
1621 if (!np) {
1622 DRM_ERROR("failed to get remote port parent");
1623 return -EINVAL;
1624 }
1625
1626 hdata->bridge = of_drm_find_bridge(np);
1627 of_node_put(np);
1628
1629 if (!hdata->bridge)
1630 return -EPROBE_DEFER;
1631
1632 return 0;
1633}
1634
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001635static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001636{
1637 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001638 int i, ret;
1639
1640 DRM_DEBUG_KMS("HDMI resource init\n");
1641
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001642 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1643 if (IS_ERR(hdata->hpd_gpio)) {
1644 DRM_ERROR("cannot get hpd gpio property\n");
1645 return PTR_ERR(hdata->hpd_gpio);
1646 }
1647
1648 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1649 if (hdata->irq < 0) {
1650 DRM_ERROR("failed to get GPIO irq\n");
1651 return hdata->irq;
1652 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001653
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001654 ret = hdmi_clk_init(hdata);
1655 if (ret)
1656 return ret;
1657
1658 ret = hdmi_clk_set_parents(hdata, false);
1659 if (ret)
1660 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001661
Milo Kimc0d656d2016-08-31 15:14:27 +09001662 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001663 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001664
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001665 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001666 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001667 if (ret != -EPROBE_DEFER)
1668 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001669 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001670 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001671
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001672 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001673
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001674 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1675 if (IS_ERR(hdata->reg_hdmi_en))
1676 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001677
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001678 ret = regulator_enable(hdata->reg_hdmi_en);
1679 if (ret) {
1680 DRM_ERROR("failed to enable hdmi-en regulator\n");
1681 return ret;
1682 }
1683 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001684
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001685 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001686}
1687
Rahul Sharma22c4f422012-10-04 20:48:55 +05301688static struct of_device_id hdmi_match_types[] = {
1689 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001690 .compatible = "samsung,exynos4210-hdmi",
1691 .data = &exynos4210_hdmi_driver_data,
1692 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301693 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001694 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301695 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301696 .compatible = "samsung,exynos5420-hdmi",
1697 .data = &exynos5420_hdmi_driver_data,
1698 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001699 .compatible = "samsung,exynos5433-hdmi",
1700 .data = &exynos5433_hdmi_driver_data,
1701 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301702 /* end node */
1703 }
1704};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001705MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301706
Inki Daef37cd5e2014-05-09 14:25:20 +09001707static int hdmi_bind(struct device *dev, struct device *master, void *data)
1708{
1709 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001710 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001711 struct drm_encoder *encoder = &hdata->encoder;
1712 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001713
Inki Daef37cd5e2014-05-09 14:25:20 +09001714 hdata->drm_dev = drm_dev;
1715
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001716 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1717 EXYNOS_DISPLAY_TYPE_HDMI);
1718 if (pipe < 0)
1719 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001720
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001721 hdata->phy_clk.enable = hdmiphy_clk_enable;
1722
1723 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1724
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001725 encoder->possible_crtcs = 1 << pipe;
1726
1727 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1728
1729 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001730 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001731
1732 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1733
1734 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001735 if (ret) {
1736 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001737 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001738 return ret;
1739 }
1740
1741 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001742}
1743
1744static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1745{
Inki Daef37cd5e2014-05-09 14:25:20 +09001746}
1747
1748static const struct component_ops hdmi_component_ops = {
1749 .bind = hdmi_bind,
1750 .unbind = hdmi_unbind,
1751};
1752
Milo Kim1caa3602016-08-31 15:14:25 +09001753static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001754{
1755 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1756 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001757 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001758
1759 np = of_find_compatible_node(NULL, NULL, compatible_str);
1760 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001761 np = of_get_next_parent(np);
1762 else
1763 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001764
Milo Kim1caa3602016-08-31 15:14:25 +09001765 if (!np) {
1766 DRM_ERROR("Failed to find ddc node in device tree\n");
1767 return -ENODEV;
1768 }
1769
1770 adpt = of_find_i2c_adapter_by_node(np);
1771 of_node_put(np);
1772
1773 if (!adpt) {
1774 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1775 return -EPROBE_DEFER;
1776 }
1777
1778 hdata->ddc_adpt = adpt;
1779
1780 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001781}
1782
Milo Kimb5413022016-08-31 15:14:26 +09001783static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001784{
1785 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001786 struct device_node *np;
1787 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001788
Milo Kimb5413022016-08-31 15:14:26 +09001789 np = of_find_compatible_node(NULL, NULL, compatible_str);
1790 if (!np) {
1791 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1792 if (!np) {
1793 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1794 return -ENODEV;
1795 }
1796 }
1797
1798 if (hdata->drv_data->is_apb_phy) {
1799 hdata->regs_hdmiphy = of_iomap(np, 0);
1800 if (!hdata->regs_hdmiphy) {
1801 DRM_ERROR("failed to ioremap hdmi phy\n");
1802 ret = -ENOMEM;
1803 goto out;
1804 }
1805 } else {
1806 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1807 if (!hdata->hdmiphy_port) {
1808 DRM_INFO("Failed to get hdmi phy i2c client\n");
1809 ret = -EPROBE_DEFER;
1810 goto out;
1811 }
1812 }
1813
1814out:
1815 of_node_put(np);
1816 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001817}
1818
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001819static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001820{
1821 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001822 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001823 struct resource *res;
1824 int ret;
1825
Andrzej Hajda930865f2014-11-17 09:54:20 +01001826 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1827 if (!hdata)
1828 return -ENOMEM;
1829
Marek Szyprowski57a64122016-04-01 15:17:44 +02001830 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001831
Andrzej Hajda930865f2014-11-17 09:54:20 +01001832 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001833
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001834 hdata->dev = dev;
1835
1836 ret = hdmi_resources_init(hdata);
1837 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001838 if (ret != -EPROBE_DEFER)
1839 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001840 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001841 }
1842
1843 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001844 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001845 if (IS_ERR(hdata->regs)) {
1846 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001847 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001848 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001849
Milo Kim1caa3602016-08-31 15:14:25 +09001850 ret = hdmi_get_ddc_adapter(hdata);
1851 if (ret)
1852 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001853
Milo Kimb5413022016-08-31 15:14:26 +09001854 ret = hdmi_get_phy_io(hdata);
1855 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001856 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001857
Sean Paul724fd142014-05-09 15:05:10 +09001858 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1859
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001860 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001861 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001862 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001863 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001864 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001865 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001866 goto err_hdmiphy;
1867 }
1868
Rahul Sharma049d34e2014-05-20 10:36:05 +05301869 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1870 "samsung,syscon-phandle");
1871 if (IS_ERR(hdata->pmureg)) {
1872 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001873 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301874 goto err_hdmiphy;
1875 }
1876
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001877 if (hdata->drv_data->has_sysreg) {
1878 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1879 "samsung,sysreg-phandle");
1880 if (IS_ERR(hdata->sysreg)) {
1881 DRM_ERROR("sysreg regmap lookup failed.\n");
1882 ret = -EPROBE_DEFER;
1883 goto err_hdmiphy;
1884 }
1885 }
1886
Hans Verkuil278c8112016-12-13 11:07:17 -02001887 hdata->notifier = cec_notifier_get(&pdev->dev);
1888 if (hdata->notifier == NULL) {
1889 ret = -ENOMEM;
1890 goto err_hdmiphy;
1891 }
1892
Sean Paulaf65c802014-01-30 16:19:27 -05001893 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001894
Inki Daedf5225b2014-05-29 18:28:02 +09001895 ret = component_add(&pdev->dev, &hdmi_component_ops);
1896 if (ret)
Hans Verkuil278c8112016-12-13 11:07:17 -02001897 goto err_notifier_put;
Inki Daedf5225b2014-05-29 18:28:02 +09001898
1899 return ret;
1900
Hans Verkuil278c8112016-12-13 11:07:17 -02001901err_notifier_put:
1902 cec_notifier_put(hdata->notifier);
Inki Daedf5225b2014-05-29 18:28:02 +09001903 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001904
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001905err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001906 if (hdata->hdmiphy_port)
1907 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301908 if (hdata->regs_hdmiphy)
1909 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001910err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001911 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001912
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001913 return ret;
1914}
1915
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001916static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001917{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001918 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001919
Sean Paul724fd142014-05-09 15:05:10 +09001920 cancel_delayed_work_sync(&hdata->hotplug_work);
Hans Verkuil278c8112016-12-13 11:07:17 -02001921 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09001922
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001923 component_del(&pdev->dev, &hdmi_component_ops);
1924
Hans Verkuil278c8112016-12-13 11:07:17 -02001925 cec_notifier_put(hdata->notifier);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001926 pm_runtime_disable(&pdev->dev);
1927
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001928 if (!IS_ERR(hdata->reg_hdmi_en))
1929 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001930
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001931 if (hdata->hdmiphy_port)
1932 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001933
Arvind Yadavd7420002016-10-19 15:34:16 +05301934 if (hdata->regs_hdmiphy)
1935 iounmap(hdata->regs_hdmiphy);
1936
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001937 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001938
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001939 return 0;
1940}
1941
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001942#ifdef CONFIG_PM
1943static int exynos_hdmi_suspend(struct device *dev)
1944{
1945 struct hdmi_context *hdata = dev_get_drvdata(dev);
1946
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001947 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001948
1949 return 0;
1950}
1951
1952static int exynos_hdmi_resume(struct device *dev)
1953{
1954 struct hdmi_context *hdata = dev_get_drvdata(dev);
1955 int ret;
1956
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001957 ret = hdmi_clk_enable_gates(hdata);
1958 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001959 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001960
1961 return 0;
1962}
1963#endif
1964
1965static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1966 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1967};
1968
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001969struct platform_driver hdmi_driver = {
1970 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001971 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001972 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301973 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001974 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001975 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301976 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001977 },
1978};