blob: 0d37d69286237055d6579272ef2fe41461c9057c [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
Inki Daef37cd5e2014-05-09 14:25:20 +090048#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090049
Sean Paul724fd142014-05-09 15:05:10 +090050#define HOTPLUG_DEBOUNCE_MS 1100
51
Rahul Sharma5a325072012-10-04 20:48:54 +053052enum hdmi_type {
53 HDMI_TYPE13,
54 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020055 HDMI_TYPE_COUNT
56};
57
58#define HDMI_MAPPED_BASE 0xffff0000
59
60enum hdmi_mapped_regs {
61 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
62 HDMI_PHY_RSTOUT,
63 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020064 HDMI_ACR_MCTS0,
65 HDMI_ACR_CTS0,
66 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020067};
68
69static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
70 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
71 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
72 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020073 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
74 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
75 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053076};
77
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020078static const char * const supply[] = {
79 "vdd",
80 "vdd_osc",
81 "vdd_pll",
82};
83
Andrzej Hajda65e98032015-11-02 14:16:41 +010084struct hdmiphy_config {
85 int pixel_clock;
86 u8 conf[32];
87};
88
89struct hdmiphy_configs {
90 int count;
91 const struct hdmiphy_config *data;
92};
93
Andrzej Hajda9be7e982016-01-14 14:22:47 +090094struct string_array_spec {
95 int count;
96 const char * const *data;
97};
98
99#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
100
Inki Daebfe4e842014-03-06 14:18:17 +0900101struct hdmi_driver_data {
102 unsigned int type;
103 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900104 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100105 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900106 struct string_array_spec clk_gates;
107 /*
108 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
109 * required parents of clock when HDMI-PHY is respectively off or on.
110 */
111 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900112};
113
Joonyoung Shim590f4182012-03-16 18:47:14 +0900114struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300115 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900116 struct device *dev;
117 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500118 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900119 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900120 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900121 struct delayed_work hotplug_work;
Hans Verkuil278c8112016-12-13 11:07:17 -0200122 struct cec_notifier *notifier;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200123 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900124
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200125 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900126 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200127 struct i2c_client *hdmiphy_port;
128 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900129 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200130 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530131 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900132 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900133 struct clk **clk_gates;
134 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200135 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
136 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900137 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100138 struct drm_bridge *bridge;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900139};
140
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300141static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100142{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900143 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100144}
145
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200146static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
147{
148 return container_of(c, struct hdmi_context, connector);
149}
150
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900151static const struct hdmiphy_config hdmiphy_v13_configs[] = {
152 {
153 .pixel_clock = 27000000,
154 .conf = {
155 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
156 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
157 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200158 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900159 },
160 },
161 {
162 .pixel_clock = 27027000,
163 .conf = {
164 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
165 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
166 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200167 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900168 },
169 },
170 {
171 .pixel_clock = 74176000,
172 .conf = {
173 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
174 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
175 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200176 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900177 },
178 },
179 {
180 .pixel_clock = 74250000,
181 .conf = {
182 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
183 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
184 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200185 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900186 },
187 },
188 {
189 .pixel_clock = 148500000,
190 .conf = {
191 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
192 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
193 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200194 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900195 },
196 },
197};
198
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500199static const struct hdmiphy_config hdmiphy_v14_configs[] = {
200 {
201 .pixel_clock = 25200000,
202 .conf = {
203 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
204 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
205 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
206 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
207 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900208 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500209 {
210 .pixel_clock = 27000000,
211 .conf = {
212 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
213 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
214 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
215 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
216 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900217 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500218 {
219 .pixel_clock = 27027000,
220 .conf = {
221 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
222 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
223 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200224 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500225 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900226 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500227 {
228 .pixel_clock = 36000000,
229 .conf = {
230 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
231 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
232 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
233 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
234 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900235 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500236 {
237 .pixel_clock = 40000000,
238 .conf = {
239 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
240 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
241 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
242 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
243 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900244 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500245 {
246 .pixel_clock = 65000000,
247 .conf = {
248 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
249 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
250 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
251 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
252 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900253 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500254 {
Shirish Se1d883c2014-03-13 14:28:27 +0900255 .pixel_clock = 71000000,
256 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530257 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
258 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
259 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900260 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
261 },
262 },
263 {
264 .pixel_clock = 73250000,
265 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530266 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
267 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
268 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900269 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
270 },
271 },
272 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500273 .pixel_clock = 74176000,
274 .conf = {
275 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
276 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
277 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
278 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
279 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900280 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500281 {
282 .pixel_clock = 74250000,
283 .conf = {
284 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
285 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
286 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200287 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500288 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900289 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500290 {
291 .pixel_clock = 83500000,
292 .conf = {
293 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
294 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
295 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
296 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
297 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900298 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500299 {
300 .pixel_clock = 106500000,
301 .conf = {
302 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
303 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
304 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
305 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
306 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900307 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500308 {
309 .pixel_clock = 108000000,
310 .conf = {
311 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
312 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
313 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
314 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
315 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900316 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500317 {
Shirish Se1d883c2014-03-13 14:28:27 +0900318 .pixel_clock = 115500000,
319 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530320 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
321 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
322 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900323 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
324 },
325 },
326 {
327 .pixel_clock = 119000000,
328 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530329 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
330 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
331 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900332 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
333 },
334 },
335 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500336 .pixel_clock = 146250000,
337 .conf = {
338 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
339 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
340 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
341 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
342 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900343 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500344 {
345 .pixel_clock = 148500000,
346 .conf = {
347 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
348 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
349 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200350 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500351 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900352 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900353};
354
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530355static const struct hdmiphy_config hdmiphy_5420_configs[] = {
356 {
357 .pixel_clock = 25200000,
358 .conf = {
359 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
360 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
361 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
362 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
363 },
364 },
365 {
366 .pixel_clock = 27000000,
367 .conf = {
368 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
369 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
370 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
371 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
372 },
373 },
374 {
375 .pixel_clock = 27027000,
376 .conf = {
377 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
378 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
379 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
380 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
381 },
382 },
383 {
384 .pixel_clock = 36000000,
385 .conf = {
386 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
387 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
388 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
389 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
390 },
391 },
392 {
393 .pixel_clock = 40000000,
394 .conf = {
395 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
396 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
397 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
398 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
399 },
400 },
401 {
402 .pixel_clock = 65000000,
403 .conf = {
404 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
405 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
406 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
407 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
408 },
409 },
410 {
411 .pixel_clock = 71000000,
412 .conf = {
413 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
414 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
415 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
416 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
417 },
418 },
419 {
420 .pixel_clock = 73250000,
421 .conf = {
422 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
423 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
424 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
425 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
426 },
427 },
428 {
429 .pixel_clock = 74176000,
430 .conf = {
431 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
432 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
433 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
434 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
435 },
436 },
437 {
438 .pixel_clock = 74250000,
439 .conf = {
440 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
441 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
442 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
443 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
444 },
445 },
446 {
447 .pixel_clock = 83500000,
448 .conf = {
449 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
450 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
451 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
452 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
453 },
454 },
455 {
456 .pixel_clock = 88750000,
457 .conf = {
458 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
459 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
460 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
461 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
462 },
463 },
464 {
465 .pixel_clock = 106500000,
466 .conf = {
467 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
468 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
469 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
470 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
471 },
472 },
473 {
474 .pixel_clock = 108000000,
475 .conf = {
476 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
477 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
478 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
479 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
480 },
481 },
482 {
483 .pixel_clock = 115500000,
484 .conf = {
485 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
486 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
487 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
488 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
489 },
490 },
491 {
492 .pixel_clock = 146250000,
493 .conf = {
494 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
495 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
496 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
497 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
498 },
499 },
500 {
501 .pixel_clock = 148500000,
502 .conf = {
503 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
504 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
505 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
506 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
507 },
508 },
509};
510
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900511static const struct hdmiphy_config hdmiphy_5433_configs[] = {
512 {
513 .pixel_clock = 27000000,
514 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100515 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
516 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
517 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900518 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
519 },
520 },
521 {
522 .pixel_clock = 27027000,
523 .conf = {
524 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100525 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
526 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
527 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900528 },
529 },
530 {
531 .pixel_clock = 40000000,
532 .conf = {
533 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
534 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
535 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
536 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
537 },
538 },
539 {
540 .pixel_clock = 50000000,
541 .conf = {
542 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
543 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
544 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
545 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
546 },
547 },
548 {
549 .pixel_clock = 65000000,
550 .conf = {
551 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
552 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
553 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
554 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
555 },
556 },
557 {
558 .pixel_clock = 74176000,
559 .conf = {
560 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
561 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
562 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
563 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
564 },
565 },
566 {
567 .pixel_clock = 74250000,
568 .conf = {
569 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
570 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
571 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
572 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
573 },
574 },
575 {
576 .pixel_clock = 108000000,
577 .conf = {
578 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
579 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
580 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
581 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
582 },
583 },
584 {
585 .pixel_clock = 148500000,
586 .conf = {
587 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
588 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
589 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
590 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
591 },
592 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100593 {
594 .pixel_clock = 297000000,
595 .conf = {
596 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
597 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
598 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
599 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
600 },
601 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900602};
603
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100604static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900605 "hdmi", "sclk_hdmi"
606};
607
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100608static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900609 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
610};
611
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900612static const char * const hdmi_clk_gates5433[] = {
613 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
614};
615
616static const char * const hdmi_clk_muxes5433[] = {
617 "oscclk", "tmds_clko", "tmds_clko_user",
618 "oscclk", "pixel_clko", "pixel_clko_user"
619};
620
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900621static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
622 .type = HDMI_TYPE13,
623 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900624 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
625 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530626};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900627
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100628static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900629 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100630 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900631 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
632 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900633};
634
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900635static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
636 .type = HDMI_TYPE14,
637 .is_apb_phy = 1,
638 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900639 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
640 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200641};
642
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900643static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
644 .type = HDMI_TYPE14,
645 .is_apb_phy = 1,
646 .has_sysreg = 1,
647 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
648 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
649 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
650};
651
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200652static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
653{
654 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
655 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
656 return reg_id;
657}
658
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900659static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
660{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200661 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900662}
663
664static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
665 u32 reg_id, u8 value)
666{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200667 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900668}
669
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200670static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
671 int bytes, u32 val)
672{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200673 reg_id = hdmi_map_reg(hdata, reg_id);
674
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200675 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200676 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200677 val >>= 8;
678 reg_id += 4;
679 }
680}
681
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100682static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
683 u8 *buf, int size)
684{
685 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
686 writel(*buf++, hdata->regs + reg_id);
687}
688
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900689static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
690 u32 reg_id, u32 value, u32 mask)
691{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200692 u32 old;
693
694 reg_id = hdmi_map_reg(hdata, reg_id);
695 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900696 value = (value & mask) | (old & ~mask);
697 writel(value, hdata->regs + reg_id);
698}
699
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900700static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
701 u32 reg_offset, const u8 *buf, u32 len)
702{
703 if ((reg_offset + len) > 32)
704 return -EINVAL;
705
706 if (hdata->hdmiphy_port) {
707 int ret;
708
709 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
710 if (ret == len)
711 return 0;
712 return ret;
713 } else {
714 int i;
715 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200716 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900717 ((reg_offset + i)<<2));
718 return 0;
719 }
720}
721
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900722static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
723{
724 int i, ret;
725
726 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
727 ret = clk_prepare_enable(hdata->clk_gates[i]);
728 if (!ret)
729 continue;
730
731 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
732 hdata->drv_data->clk_gates.data[i], ret);
733 while (i--)
734 clk_disable_unprepare(hdata->clk_gates[i]);
735 return ret;
736 }
737
738 return 0;
739}
740
741static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
742{
743 int i = hdata->drv_data->clk_gates.count;
744
745 while (i--)
746 clk_disable_unprepare(hdata->clk_gates[i]);
747}
748
749static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
750{
751 struct device *dev = hdata->dev;
752 int ret = 0;
753 int i;
754
755 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
756 struct clk **c = &hdata->clk_muxes[i];
757
758 ret = clk_set_parent(c[2], c[to_phy]);
759 if (!ret)
760 continue;
761
762 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
763 hdata->drv_data->clk_muxes.data[i + 2],
764 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
765 }
766
767 return ret;
768}
769
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100770static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530771{
Andrzej Hajda991ea042017-09-29 12:05:37 +0200772 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100773 union hdmi_infoframe frm;
774 u8 buf[25];
775 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530776
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530777 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530778 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
779 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100780 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
781 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530782 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
783 return;
784 }
785
Andrzej Hajda991ea042017-09-29 12:05:37 +0200786 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi, m, false);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100787 if (!ret)
788 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
789 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530790 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100791 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
792 } else {
793 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
794 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530795
Andrzej Hajda991ea042017-09-29 12:05:37 +0200796 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi, m);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100797 if (!ret)
798 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
799 sizeof(buf));
800 if (ret > 0) {
801 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100802 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
803 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100804 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530805
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100806 ret = hdmi_audio_infoframe_init(&frm.audio);
807 if (!ret) {
808 frm.audio.channels = 2;
809 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
810 }
811 if (ret > 0) {
812 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
813 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530814 }
815}
816
Sean Pauld9716ee2014-01-30 16:19:29 -0500817static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
818 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500819{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200820 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500821
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200822 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200823 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530824
Hans Verkuil278c8112016-12-13 11:07:17 -0200825 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200826 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500827}
828
Sean Pauld9716ee2014-01-30 16:19:29 -0500829static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900830{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200831 drm_connector_unregister(connector);
832 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900833}
834
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100835static const struct drm_connector_funcs hdmi_connector_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500836 .fill_modes = drm_helper_probe_single_connector_modes,
837 .detect = hdmi_detect,
838 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300839 .reset = drm_atomic_helper_connector_reset,
840 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
841 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500842};
843
844static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900845{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200846 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500847 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200848 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900849
Inki Dae8fa04aa2014-03-13 16:38:31 +0900850 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500851 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900852
Inki Dae8fa04aa2014-03-13 16:38:31 +0900853 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500854 if (!edid)
855 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900856
Sean Pauld9716ee2014-01-30 16:19:29 -0500857 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500858 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
859 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500860 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500861
Sean Pauld9716ee2014-01-30 16:19:29 -0500862 drm_mode_connector_update_edid_property(connector, edid);
Hans Verkuil278c8112016-12-13 11:07:17 -0200863 cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
Sean Pauld9716ee2014-01-30 16:19:29 -0500864
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200865 ret = drm_add_edid_modes(connector, edid);
866
867 kfree(edid);
868
869 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870}
871
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900872static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900873{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100874 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900875 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900876
Andrzej Hajda65e98032015-11-02 14:16:41 +0100877 for (i = 0; i < confs->count; i++)
878 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500879 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500880
881 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
882 return -EINVAL;
883}
884
Sean Pauld9716ee2014-01-30 16:19:29 -0500885static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500886 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900887{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200888 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900889 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900890
Rahul Sharma16844fb2013-06-10 14:50:00 +0530891 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
892 mode->hdisplay, mode->vdisplay, mode->vrefresh,
893 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
894 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900895
Rahul Sharma16844fb2013-06-10 14:50:00 +0530896 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900897 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500898 return MODE_BAD;
899
900 return MODE_OK;
901}
902
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100903static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500904 .get_modes = hdmi_get_modes,
905 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500906};
907
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300908static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500909{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300910 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500911 struct drm_connector *connector = &hdata->connector;
912 int ret;
913
Sean Pauld9716ee2014-01-30 16:19:29 -0500914 connector->interlace_allowed = true;
915 connector->polled = DRM_CONNECTOR_POLL_HPD;
916
917 ret = drm_connector_init(hdata->drm_dev, connector,
918 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
919 if (ret) {
920 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900921 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500922 }
923
924 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300925 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500926
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100927 if (hdata->bridge) {
928 encoder->bridge = hdata->bridge;
929 hdata->bridge->encoder = encoder;
930 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
931 if (ret)
932 DRM_ERROR("Failed to attach bridge\n");
933 }
934
935 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500936}
937
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300938static bool hdmi_mode_fixup(struct drm_encoder *encoder,
939 const struct drm_display_mode *mode,
940 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500941{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300942 struct drm_device *dev = encoder->dev;
943 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500944 struct drm_display_mode *m;
Maciej Purski04fc52f2017-09-05 14:23:02 +0200945 struct drm_connector_list_iter conn_iter;
Sean Paulf041b252014-01-30 16:19:15 -0500946 int mode_ok;
947
Sean Paulf041b252014-01-30 16:19:15 -0500948 drm_mode_set_crtcinfo(adjusted_mode, 0);
949
Maciej Purski04fc52f2017-09-05 14:23:02 +0200950 drm_connector_list_iter_begin(dev, &conn_iter);
951 drm_for_each_connector_iter(connector, &conn_iter) {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300952 if (connector->encoder == encoder)
953 break;
954 }
Maciej Purski04fc52f2017-09-05 14:23:02 +0200955 if (connector)
956 drm_connector_get(connector);
957 drm_connector_list_iter_end(&conn_iter);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300958
Maciej Purski04fc52f2017-09-05 14:23:02 +0200959 if (!connector)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300960 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)
Maciej Purski04fc52f2017-09-05 14:23:02 +0200965 goto cleanup;
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
Maciej Purski04fc52f2017-09-05 14:23:02 +0200985cleanup:
986 drm_connector_put(connector);
987
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300988 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500989}
990
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200991static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900992{
993 u32 n, cts;
994
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200995 cts = (freq % 9) ? 27000 : 30000;
996 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900997
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200998 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
999 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
1000 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001001 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001002}
1003
1004static void hdmi_audio_init(struct hdmi_context *hdata)
1005{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301006 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001007 u32 data_num, bit_ch, sample_frq;
1008 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001009
1010 sample_rate = 44100;
1011 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001012
1013 switch (bits_per_sample) {
1014 case 20:
1015 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001016 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001017 break;
1018 case 24:
1019 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001020 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001021 break;
1022 default:
1023 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001024 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001025 break;
1026 }
1027
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001028 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001029
1030 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1031 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1032 | HDMI_I2S_MUX_ENABLE);
1033
1034 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1035 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1036
1037 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1038
1039 sample_frq = (sample_rate == 44100) ? 0 :
1040 (sample_rate == 48000) ? 2 :
1041 (sample_rate == 32000) ? 3 :
1042 (sample_rate == 96000) ? 0xa : 0x0;
1043
1044 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1045 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1046
1047 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1048 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1049
1050 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1051 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1052 | HDMI_I2S_SEL_LRCK(6));
1053 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1054 | HDMI_I2S_SEL_SDATA2(4));
1055 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1056 | HDMI_I2S_SEL_SDATA2(2));
1057 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1058
1059 /* I2S_CON_1 & 2 */
1060 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1061 | HDMI_I2S_L_CH_LOW_POL);
1062 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1063 | HDMI_I2S_SET_BIT_CH(bit_ch)
1064 | HDMI_I2S_SET_SDATA_BIT(data_num)
1065 | HDMI_I2S_BASIC_FORMAT);
1066
1067 /* Configure register related to CUV information */
1068 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1069 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1070 | HDMI_I2S_COPYRIGHT
1071 | HDMI_I2S_LINEAR_PCM
1072 | HDMI_I2S_CONSUMER_FORMAT);
1073 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1074 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1075 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1076 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1077 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1078 HDMI_I2S_ORG_SMP_FREQ_44_1
1079 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1080 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1081
1082 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1083}
1084
1085static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1086{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001087 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001088 return;
1089
1090 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1091 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1092 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1093}
1094
Rahul Sharmabfa48422014-04-03 20:41:04 +05301095static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001096{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001097 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Rahul Sharmabfa48422014-04-03 20:41:04 +05301098 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001099
Andrzej Hajda991ea042017-09-29 12:05:37 +02001100 if (m->flags & DRM_MODE_FLAG_INTERLACE)
Rahul Sharmabfa48422014-04-03 20:41:04 +05301101 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001102
Rahul Sharmabfa48422014-04-03 20:41:04 +05301103 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1104 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001105}
1106
1107static void hdmi_conf_init(struct hdmi_context *hdata)
1108{
Sean Paul77006a72013-01-16 10:17:20 -05001109 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001110 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1111 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001112
1113 /* choose HDMI mode */
1114 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1115 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001116 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301117 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001118 /* disable bluescreen */
1119 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001120
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001121 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001122 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1123 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1124 hdmi_reg_writeb(hdata, HDMI_CON_2,
1125 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1126 }
1127
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001128 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001129 /* choose bluescreen (fecal) color */
1130 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1131 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1132 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1133
1134 /* enable AVI packet every vsync, fixes purple line problem */
1135 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1136 /* force RGB, look to CEA-861-D, table 7 for more detail */
1137 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1138 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1139
1140 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1141 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1142 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1143 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001144 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301145
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001146 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001147 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1148 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001149}
1150
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001151static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1152{
1153 int tries;
1154
1155 for (tries = 0; tries < 10; ++tries) {
1156 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1157
1158 if (val & HDMI_PHY_STATUS_READY) {
1159 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1160 return;
1161 }
1162 usleep_range(10, 20);
1163 }
1164
1165 DRM_ERROR("PLL could not reach steady state\n");
1166}
1167
Rahul Sharma16844fb2013-06-10 14:50:00 +05301168static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001169{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001170 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001171 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001172
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001173 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1174 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1175 (m->htotal << 12) | m->vtotal);
1176
1177 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1178 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1179
1180 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1181 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1182
1183 val = (m->hsync_start - m->hdisplay - 2);
1184 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001185 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001186 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1187
1188 /*
1189 * Quirk requirement for exynos HDMI IP design,
1190 * 2 pixels less than the actual calculation for hsync_start
1191 * and end.
1192 */
1193
1194 /* Following values & calculations differ for different type of modes */
1195 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001196 val = ((m->vsync_end - m->vdisplay) / 2);
1197 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1198 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1199
1200 val = m->vtotal / 2;
1201 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1202 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1203
1204 val = (m->vtotal +
1205 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1206 val |= m->vtotal << 11;
1207 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1208
1209 val = ((m->vtotal / 2) + 7);
1210 val |= ((m->vtotal / 2) + 2) << 12;
1211 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1212
1213 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1214 val |= ((m->htotal / 2) +
1215 (m->hsync_start - m->hdisplay)) << 12;
1216 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1217
1218 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1219 (m->vtotal - m->vdisplay) / 2);
1220 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1221
1222 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1223 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001224 val = m->vtotal;
1225 val |= (m->vtotal - m->vdisplay) << 11;
1226 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1227
1228 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1229
1230 val = (m->vsync_end - m->vdisplay);
1231 val |= ((m->vsync_start - m->vdisplay) << 12);
1232 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1233
1234 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1235 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1236 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1237 m->vtotal - m->vdisplay);
1238 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001239 }
1240
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001241 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1242 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1243 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1244 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001245}
1246
Rahul Sharma16844fb2013-06-10 14:50:00 +05301247static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001248{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001249 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001250
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001251 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1252 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1253 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1254 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001255 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001256 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1257 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1258 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1259 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1260
1261 /*
1262 * Quirk requirement for exynos 5 HDMI IP design,
1263 * 2 pixels less than the actual calculation for hsync_start
1264 * and end.
1265 */
1266
1267 /* Following values & calculations differ for different type of modes */
1268 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001269 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1270 (m->vsync_end - m->vdisplay) / 2);
1271 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1272 (m->vsync_start - m->vdisplay) / 2);
1273 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1274 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1275 (m->vtotal - m->vdisplay) / 2);
1276 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1277 m->vtotal - m->vdisplay / 2);
1278 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1279 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1280 (m->vtotal / 2) + 7);
1281 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1282 (m->vtotal / 2) + 2);
1283 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1284 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1285 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1286 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1287 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1288 (m->vtotal - m->vdisplay) / 2);
1289 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1290 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1291 m->vtotal - m->vdisplay / 2);
1292 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1293 (m->vtotal / 2) + 1);
1294 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1295 (m->vtotal / 2) + 1);
1296 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1297 (m->vtotal / 2) + 1);
1298 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1299 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1300 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001301 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1302 m->vsync_end - m->vdisplay);
1303 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1304 m->vsync_start - m->vdisplay);
1305 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1306 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1307 m->vtotal - m->vdisplay);
1308 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1309 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1310 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1311 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1312 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1313 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1314 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1315 m->vtotal - m->vdisplay);
1316 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001317 }
1318
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001319 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1320 m->hsync_start - m->hdisplay - 2);
1321 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1322 m->hsync_end - m->hdisplay - 2);
1323 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1327 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1328 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1329 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1330 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1331 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1332 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1333 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1334 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1335 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1336 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1337 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1338 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1339 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1340 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001341
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001342 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1343 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1344 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1345 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001346 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1347 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001348}
1349
Rahul Sharma16844fb2013-06-10 14:50:00 +05301350static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001351{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001352 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301353 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001354 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301355 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001356
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001357 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001358}
1359
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001360static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1361{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001362 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1363 usleep_range(10000, 12000);
1364 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1365 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001366 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001367 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001368 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001369 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001370}
1371
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001372static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1373{
1374 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1375
1376 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1377 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1378}
1379
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001380static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1381{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001382 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001383 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001384 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001385
Andrzej Hajda991ea042017-09-29 12:05:37 +02001386 ret = hdmi_find_phy_conf(hdata, m->clock * 1000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001387 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001388 DRM_ERROR("failed to find hdmiphy conf\n");
1389 return;
1390 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001391 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1392
1393 hdmi_clk_set_parents(hdata, false);
1394
1395 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001396
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001397 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001398 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001399 if (ret) {
1400 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001401 return;
1402 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001403 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001404 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001405 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001406 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001407}
1408
1409static void hdmi_conf_apply(struct hdmi_context *hdata)
1410{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301411 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001412 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001413 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301414 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001415 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001416}
1417
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001418static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1419{
1420 if (!hdata->sysreg)
1421 return;
1422
1423 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1424 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1425}
1426
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001427static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001428{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001429 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001430 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001431
Sean Paulaf65c802014-01-30 16:19:27 -05001432 pm_runtime_get_sync(hdata->dev);
1433
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001434 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001435 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1436
Rahul Sharma049d34e2014-05-20 10:36:05 +05301437 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1438 PMU_HDMI_PHY_ENABLE_BIT, 1);
1439
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001440 hdmi_set_refclk(hdata, true);
1441
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001442 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1443
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001444 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001445
1446 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001447}
1448
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001449static void hdmiphy_disable(struct hdmi_context *hdata)
1450{
1451 if (!hdata->powered)
1452 return;
1453
1454 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1455
1456 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1457
1458 hdmi_set_refclk(hdata, false);
1459
1460 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1461 PMU_HDMI_PHY_ENABLE_BIT, 0);
1462
1463 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1464
1465 pm_runtime_put_sync(hdata->dev);
1466
1467 hdata->powered = false;
1468}
1469
1470static void hdmi_enable(struct drm_encoder *encoder)
1471{
1472 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1473
1474 hdmiphy_enable(hdata);
1475 hdmi_conf_apply(hdata);
1476}
1477
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001478static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001479{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001480 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001481
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001482 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001483 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001484
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001485 /*
1486 * The SFRs of VP and Mixer are updated by Vertical Sync of
1487 * Timing generator which is a part of HDMI so the sequence
1488 * to disable TV Subsystem should be as following,
1489 * VP -> Mixer -> HDMI
1490 *
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001491 * To achieve such sequence HDMI is disabled together with HDMI PHY, via
1492 * pipe clock callback.
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001493 */
Sean Paul724fd142014-05-09 15:05:10 +09001494 cancel_delayed_work(&hdata->hotplug_work);
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001495 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001496}
1497
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001498static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001499 .mode_fixup = hdmi_mode_fixup,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001500 .enable = hdmi_enable,
1501 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001502};
1503
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001504static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001505 .destroy = drm_encoder_cleanup,
1506};
1507
Sean Paul724fd142014-05-09 15:05:10 +09001508static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001509{
Sean Paul724fd142014-05-09 15:05:10 +09001510 struct hdmi_context *hdata;
1511
1512 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001513
Sean Paul45517892014-01-30 16:19:05 -05001514 if (hdata->drm_dev)
1515 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001516}
1517
1518static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1519{
1520 struct hdmi_context *hdata = arg;
1521
1522 mod_delayed_work(system_wq, &hdata->hotplug_work,
1523 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001524
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001525 return IRQ_HANDLED;
1526}
1527
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001528static int hdmi_clks_get(struct hdmi_context *hdata,
1529 const struct string_array_spec *names,
1530 struct clk **clks)
1531{
1532 struct device *dev = hdata->dev;
1533 int i;
1534
1535 for (i = 0; i < names->count; ++i) {
1536 struct clk *clk = devm_clk_get(dev, names->data[i]);
1537
1538 if (IS_ERR(clk)) {
1539 int ret = PTR_ERR(clk);
1540
1541 dev_err(dev, "Cannot get clock %s, %d\n",
1542 names->data[i], ret);
1543
1544 return ret;
1545 }
1546
1547 clks[i] = clk;
1548 }
1549
1550 return 0;
1551}
1552
1553static int hdmi_clk_init(struct hdmi_context *hdata)
1554{
1555 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1556 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1557 struct device *dev = hdata->dev;
1558 struct clk **clks;
1559 int ret;
1560
1561 if (!count)
1562 return 0;
1563
1564 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1565 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001566 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001567
1568 hdata->clk_gates = clks;
1569 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1570
1571 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1572 if (ret)
1573 return ret;
1574
1575 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1576}
1577
1578
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001579static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1580{
1581 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1582 phy_clk);
1583
1584 if (enable)
1585 hdmiphy_enable(hdata);
1586 else
1587 hdmiphy_disable(hdata);
1588}
1589
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001590static int hdmi_bridge_init(struct hdmi_context *hdata)
1591{
1592 struct device *dev = hdata->dev;
1593 struct device_node *ep, *np;
1594
1595 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1596 if (!ep)
1597 return 0;
1598
1599 np = of_graph_get_remote_port_parent(ep);
1600 of_node_put(ep);
1601 if (!np) {
1602 DRM_ERROR("failed to get remote port parent");
1603 return -EINVAL;
1604 }
1605
1606 hdata->bridge = of_drm_find_bridge(np);
1607 of_node_put(np);
1608
1609 if (!hdata->bridge)
1610 return -EPROBE_DEFER;
1611
1612 return 0;
1613}
1614
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001615static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001616{
1617 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001618 int i, ret;
1619
1620 DRM_DEBUG_KMS("HDMI resource init\n");
1621
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001622 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1623 if (IS_ERR(hdata->hpd_gpio)) {
1624 DRM_ERROR("cannot get hpd gpio property\n");
1625 return PTR_ERR(hdata->hpd_gpio);
1626 }
1627
1628 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1629 if (hdata->irq < 0) {
1630 DRM_ERROR("failed to get GPIO irq\n");
1631 return hdata->irq;
1632 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001633
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001634 ret = hdmi_clk_init(hdata);
1635 if (ret)
1636 return ret;
1637
1638 ret = hdmi_clk_set_parents(hdata, false);
1639 if (ret)
1640 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001641
Milo Kimc0d656d2016-08-31 15:14:27 +09001642 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001643 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001644
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001645 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001646 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001647 if (ret != -EPROBE_DEFER)
1648 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001649 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001650 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001651
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001652 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001653
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001654 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1655 if (IS_ERR(hdata->reg_hdmi_en))
1656 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001657
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001658 ret = regulator_enable(hdata->reg_hdmi_en);
1659 if (ret) {
1660 DRM_ERROR("failed to enable hdmi-en regulator\n");
1661 return ret;
1662 }
1663 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001664
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001665 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001666}
1667
Arvind Yadave3cc51e2017-06-19 14:54:03 +05301668static const struct of_device_id hdmi_match_types[] = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301669 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001670 .compatible = "samsung,exynos4210-hdmi",
1671 .data = &exynos4210_hdmi_driver_data,
1672 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301673 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001674 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301675 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301676 .compatible = "samsung,exynos5420-hdmi",
1677 .data = &exynos5420_hdmi_driver_data,
1678 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001679 .compatible = "samsung,exynos5433-hdmi",
1680 .data = &exynos5433_hdmi_driver_data,
1681 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301682 /* end node */
1683 }
1684};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001685MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301686
Inki Daef37cd5e2014-05-09 14:25:20 +09001687static int hdmi_bind(struct device *dev, struct device *master, void *data)
1688{
1689 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001690 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001691 struct drm_encoder *encoder = &hdata->encoder;
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001692 struct exynos_drm_crtc *crtc;
1693 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001694
Inki Daef37cd5e2014-05-09 14:25:20 +09001695 hdata->drm_dev = drm_dev;
1696
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001697 hdata->phy_clk.enable = hdmiphy_clk_enable;
1698
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001699 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001700 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001701
1702 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1703
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001704 ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_HDMI);
1705 if (ret < 0)
1706 return ret;
1707
1708 crtc = exynos_drm_crtc_get_by_type(drm_dev, EXYNOS_DISPLAY_TYPE_HDMI);
1709 crtc->pipe_clk = &hdata->phy_clk;
1710
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001711 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001712 if (ret) {
1713 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001714 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001715 return ret;
1716 }
1717
1718 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001719}
1720
1721static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1722{
Inki Daef37cd5e2014-05-09 14:25:20 +09001723}
1724
1725static const struct component_ops hdmi_component_ops = {
1726 .bind = hdmi_bind,
1727 .unbind = hdmi_unbind,
1728};
1729
Milo Kim1caa3602016-08-31 15:14:25 +09001730static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001731{
1732 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1733 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001734 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001735
1736 np = of_find_compatible_node(NULL, NULL, compatible_str);
1737 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001738 np = of_get_next_parent(np);
1739 else
1740 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001741
Milo Kim1caa3602016-08-31 15:14:25 +09001742 if (!np) {
1743 DRM_ERROR("Failed to find ddc node in device tree\n");
1744 return -ENODEV;
1745 }
1746
1747 adpt = of_find_i2c_adapter_by_node(np);
1748 of_node_put(np);
1749
1750 if (!adpt) {
1751 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1752 return -EPROBE_DEFER;
1753 }
1754
1755 hdata->ddc_adpt = adpt;
1756
1757 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001758}
1759
Milo Kimb5413022016-08-31 15:14:26 +09001760static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001761{
1762 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001763 struct device_node *np;
1764 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001765
Milo Kimb5413022016-08-31 15:14:26 +09001766 np = of_find_compatible_node(NULL, NULL, compatible_str);
1767 if (!np) {
1768 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1769 if (!np) {
1770 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1771 return -ENODEV;
1772 }
1773 }
1774
1775 if (hdata->drv_data->is_apb_phy) {
1776 hdata->regs_hdmiphy = of_iomap(np, 0);
1777 if (!hdata->regs_hdmiphy) {
1778 DRM_ERROR("failed to ioremap hdmi phy\n");
1779 ret = -ENOMEM;
1780 goto out;
1781 }
1782 } else {
1783 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1784 if (!hdata->hdmiphy_port) {
1785 DRM_INFO("Failed to get hdmi phy i2c client\n");
1786 ret = -EPROBE_DEFER;
1787 goto out;
1788 }
1789 }
1790
1791out:
1792 of_node_put(np);
1793 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001794}
1795
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001796static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001797{
1798 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001799 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001800 struct resource *res;
1801 int ret;
1802
Andrzej Hajda930865f2014-11-17 09:54:20 +01001803 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1804 if (!hdata)
1805 return -ENOMEM;
1806
Marek Szyprowski57a64122016-04-01 15:17:44 +02001807 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001808
Andrzej Hajda930865f2014-11-17 09:54:20 +01001809 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001810
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001811 hdata->dev = dev;
1812
1813 ret = hdmi_resources_init(hdata);
1814 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001815 if (ret != -EPROBE_DEFER)
1816 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001817 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001818 }
1819
1820 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001821 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001822 if (IS_ERR(hdata->regs)) {
1823 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001824 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001825 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001826
Milo Kim1caa3602016-08-31 15:14:25 +09001827 ret = hdmi_get_ddc_adapter(hdata);
1828 if (ret)
1829 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001830
Milo Kimb5413022016-08-31 15:14:26 +09001831 ret = hdmi_get_phy_io(hdata);
1832 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001833 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001834
Sean Paul724fd142014-05-09 15:05:10 +09001835 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1836
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001837 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001838 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001839 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001840 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001841 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001842 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001843 goto err_hdmiphy;
1844 }
1845
Rahul Sharma049d34e2014-05-20 10:36:05 +05301846 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1847 "samsung,syscon-phandle");
1848 if (IS_ERR(hdata->pmureg)) {
1849 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001850 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301851 goto err_hdmiphy;
1852 }
1853
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001854 if (hdata->drv_data->has_sysreg) {
1855 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1856 "samsung,sysreg-phandle");
1857 if (IS_ERR(hdata->sysreg)) {
1858 DRM_ERROR("sysreg regmap lookup failed.\n");
1859 ret = -EPROBE_DEFER;
1860 goto err_hdmiphy;
1861 }
1862 }
1863
Hans Verkuil278c8112016-12-13 11:07:17 -02001864 hdata->notifier = cec_notifier_get(&pdev->dev);
1865 if (hdata->notifier == NULL) {
1866 ret = -ENOMEM;
1867 goto err_hdmiphy;
1868 }
1869
Sean Paulaf65c802014-01-30 16:19:27 -05001870 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001871
Inki Daedf5225b2014-05-29 18:28:02 +09001872 ret = component_add(&pdev->dev, &hdmi_component_ops);
1873 if (ret)
Hans Verkuil278c8112016-12-13 11:07:17 -02001874 goto err_notifier_put;
Inki Daedf5225b2014-05-29 18:28:02 +09001875
1876 return ret;
1877
Hans Verkuil278c8112016-12-13 11:07:17 -02001878err_notifier_put:
1879 cec_notifier_put(hdata->notifier);
Inki Daedf5225b2014-05-29 18:28:02 +09001880 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001881
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001882err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001883 if (hdata->hdmiphy_port)
1884 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301885 if (hdata->regs_hdmiphy)
1886 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001887err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001888 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001889
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001890 return ret;
1891}
1892
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001893static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001894{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001895 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001896
Sean Paul724fd142014-05-09 15:05:10 +09001897 cancel_delayed_work_sync(&hdata->hotplug_work);
Hans Verkuil278c8112016-12-13 11:07:17 -02001898 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09001899
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001900 component_del(&pdev->dev, &hdmi_component_ops);
1901
Hans Verkuil278c8112016-12-13 11:07:17 -02001902 cec_notifier_put(hdata->notifier);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001903 pm_runtime_disable(&pdev->dev);
1904
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001905 if (!IS_ERR(hdata->reg_hdmi_en))
1906 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001907
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001908 if (hdata->hdmiphy_port)
1909 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001910
Arvind Yadavd7420002016-10-19 15:34:16 +05301911 if (hdata->regs_hdmiphy)
1912 iounmap(hdata->regs_hdmiphy);
1913
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001914 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001915
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001916 return 0;
1917}
1918
Arnd Bergmann7e175102017-07-21 22:47:18 +02001919static int __maybe_unused exynos_hdmi_suspend(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001920{
1921 struct hdmi_context *hdata = dev_get_drvdata(dev);
1922
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001923 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001924
1925 return 0;
1926}
1927
Arnd Bergmann7e175102017-07-21 22:47:18 +02001928static int __maybe_unused exynos_hdmi_resume(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001929{
1930 struct hdmi_context *hdata = dev_get_drvdata(dev);
1931 int ret;
1932
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001933 ret = hdmi_clk_enable_gates(hdata);
1934 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001935 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001936
1937 return 0;
1938}
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001939
1940static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1941 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1942};
1943
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001944struct platform_driver hdmi_driver = {
1945 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001946 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001947 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301948 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001949 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001950 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301951 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001952 },
1953};