blob: 894a997936332f0813e711febf6dc0a87f0f7b95 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002/*
3 * Copyright (C) 2011 Samsung Electronics Co.Ltd
4 * Authors:
5 * Seung-Woo Kim <sw0312.kim@samsung.com>
6 * Inki Dae <inki.dae@samsung.com>
7 * Joonyoung Shim <jy0922.shim@samsung.com>
8 *
9 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
Seung-Woo Kimd8408322011-12-21 17:39:39 +090010 */
11
David Howells760285e2012-10-02 18:01:07 +010012#include <drm/drmP.h>
Gustavo Padovan4ea95262015-06-01 12:04:44 -030013#include <drm/drm_atomic_helper.h>
Daniel Vetterfcd70cd2019-01-17 22:03:34 +010014#include <drm/drm_edid.h>
15#include <drm/drm_probe_helper.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090016
17#include "regs-hdmi.h"
18
19#include <linux/kernel.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090020#include <linux/wait.h>
21#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090022#include <linux/platform_device.h>
23#include <linux/interrupt.h>
24#include <linux/irq.h>
25#include <linux/delay.h>
26#include <linux/pm_runtime.h>
27#include <linux/clk.h>
Andrzej Hajda2228b7c2015-09-25 14:48:24 +020028#include <linux/gpio/consumer.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090029#include <linux/regulator/consumer.h>
Rahul Sharma22c4f422012-10-04 20:48:55 +053030#include <linux/io.h>
Rahul Sharmad5e9ca42014-05-09 15:34:18 +090031#include <linux/of_address.h>
Andrzej Hajdacd240cd2015-07-09 16:28:09 +020032#include <linux/of_device.h>
Andrzej Hajdaaa181572017-02-01 09:29:14 +010033#include <linux/of_graph.h>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053034#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090035#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053036#include <linux/mfd/syscon.h>
37#include <linux/regmap.h>
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +020038#include <sound/hdmi-codec.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090039#include <drm/exynos_drm.h>
40
Hans Verkuil278c8112016-12-13 11:07:17 -020041#include <media/cec-notifier.h>
42
Inki Daef37cd5e2014-05-09 14:25:20 +090043#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090044
Sean Paul724fd142014-05-09 15:05:10 +090045#define HOTPLUG_DEBOUNCE_MS 1100
46
Rahul Sharma5a325072012-10-04 20:48:54 +053047enum hdmi_type {
48 HDMI_TYPE13,
49 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020050 HDMI_TYPE_COUNT
51};
52
53#define HDMI_MAPPED_BASE 0xffff0000
54
55enum hdmi_mapped_regs {
56 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
57 HDMI_PHY_RSTOUT,
58 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020059 HDMI_ACR_MCTS0,
60 HDMI_ACR_CTS0,
61 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020062};
63
64static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
65 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
66 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
67 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020068 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
69 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
70 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053071};
72
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020073static const char * const supply[] = {
74 "vdd",
75 "vdd_osc",
76 "vdd_pll",
77};
78
Andrzej Hajda65e98032015-11-02 14:16:41 +010079struct hdmiphy_config {
80 int pixel_clock;
81 u8 conf[32];
82};
83
84struct hdmiphy_configs {
85 int count;
86 const struct hdmiphy_config *data;
87};
88
Andrzej Hajda9be7e982016-01-14 14:22:47 +090089struct string_array_spec {
90 int count;
91 const char * const *data;
92};
93
94#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
95
Inki Daebfe4e842014-03-06 14:18:17 +090096struct hdmi_driver_data {
97 unsigned int type;
98 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +090099 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100100 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900101 struct string_array_spec clk_gates;
102 /*
103 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
104 * required parents of clock when HDMI-PHY is respectively off or on.
105 */
106 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900107};
108
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +0200109struct hdmi_audio {
110 struct platform_device *pdev;
111 struct hdmi_audio_infoframe infoframe;
112 struct hdmi_codec_params params;
113 bool mute;
114};
115
Joonyoung Shim590f4182012-03-16 18:47:14 +0900116struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300117 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900118 struct device *dev;
119 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500120 struct drm_connector connector;
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;
Hans Verkuil278c8112016-12-13 11:07:17 -0200123 struct cec_notifier *notifier;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200124 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900125
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200126 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900127 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200128 struct i2c_client *hdmiphy_port;
129 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900130 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200131 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530132 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900133 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900134 struct clk **clk_gates;
135 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200136 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
137 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900138 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100139 struct drm_bridge *bridge;
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +0200140
141 /* mutex protecting subsequent fields below */
142 struct mutex mutex;
143 struct hdmi_audio audio;
144 bool powered;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900145};
146
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300147static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100148{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900149 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100150}
151
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200152static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
153{
154 return container_of(c, struct hdmi_context, connector);
155}
156
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900157static const struct hdmiphy_config hdmiphy_v13_configs[] = {
158 {
159 .pixel_clock = 27000000,
160 .conf = {
161 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
162 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
163 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200164 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900165 },
166 },
167 {
168 .pixel_clock = 27027000,
169 .conf = {
170 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
171 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
172 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200173 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900174 },
175 },
176 {
177 .pixel_clock = 74176000,
178 .conf = {
179 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
180 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
181 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200182 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900183 },
184 },
185 {
186 .pixel_clock = 74250000,
187 .conf = {
188 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
189 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
190 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200191 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900192 },
193 },
194 {
195 .pixel_clock = 148500000,
196 .conf = {
197 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
198 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
199 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200200 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900201 },
202 },
203};
204
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500205static const struct hdmiphy_config hdmiphy_v14_configs[] = {
206 {
207 .pixel_clock = 25200000,
208 .conf = {
209 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
210 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
211 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
212 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
213 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900214 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500215 {
216 .pixel_clock = 27000000,
217 .conf = {
218 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
219 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
220 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
221 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
222 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900223 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500224 {
225 .pixel_clock = 27027000,
226 .conf = {
227 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
228 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
229 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200230 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500231 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900232 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500233 {
234 .pixel_clock = 36000000,
235 .conf = {
236 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
237 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
238 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
239 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
240 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900241 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500242 {
243 .pixel_clock = 40000000,
244 .conf = {
245 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
246 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
247 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
248 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
249 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900250 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500251 {
252 .pixel_clock = 65000000,
253 .conf = {
254 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
255 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
256 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
257 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
258 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900259 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500260 {
Shirish Se1d883c2014-03-13 14:28:27 +0900261 .pixel_clock = 71000000,
262 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530263 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
264 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
265 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900266 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
267 },
268 },
269 {
270 .pixel_clock = 73250000,
271 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530272 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
273 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
274 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900275 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
276 },
277 },
278 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500279 .pixel_clock = 74176000,
280 .conf = {
281 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
282 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
283 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
284 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
285 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900286 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500287 {
288 .pixel_clock = 74250000,
289 .conf = {
290 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
291 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
292 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200293 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500294 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900295 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500296 {
297 .pixel_clock = 83500000,
298 .conf = {
299 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
300 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
301 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
302 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
303 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900304 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500305 {
Daniel Drakeae58c032017-09-29 12:05:42 +0200306 .pixel_clock = 85500000,
307 .conf = {
308 0x01, 0xd1, 0x24, 0x11, 0x40, 0x40, 0xd0, 0x08,
309 0x84, 0xa0, 0xd6, 0xd8, 0x45, 0xa0, 0xac, 0x80,
310 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
311 0x54, 0x90, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
312 },
313 },
314 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500315 .pixel_clock = 106500000,
316 .conf = {
317 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
318 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
319 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
320 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
321 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900322 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500323 {
324 .pixel_clock = 108000000,
325 .conf = {
326 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
327 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
328 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
329 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
330 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900331 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500332 {
Shirish Se1d883c2014-03-13 14:28:27 +0900333 .pixel_clock = 115500000,
334 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530335 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
336 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
337 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900338 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
339 },
340 },
341 {
342 .pixel_clock = 119000000,
343 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530344 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
345 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
346 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900347 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
348 },
349 },
350 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500351 .pixel_clock = 146250000,
352 .conf = {
353 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
354 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
355 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
356 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
357 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900358 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500359 {
360 .pixel_clock = 148500000,
361 .conf = {
362 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
363 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
364 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200365 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500366 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900367 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900368};
369
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530370static const struct hdmiphy_config hdmiphy_5420_configs[] = {
371 {
372 .pixel_clock = 25200000,
373 .conf = {
374 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
375 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
376 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
377 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
378 },
379 },
380 {
381 .pixel_clock = 27000000,
382 .conf = {
383 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
384 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
385 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
386 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
387 },
388 },
389 {
390 .pixel_clock = 27027000,
391 .conf = {
392 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
393 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
394 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
395 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
396 },
397 },
398 {
399 .pixel_clock = 36000000,
400 .conf = {
401 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
402 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
403 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
404 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
405 },
406 },
407 {
408 .pixel_clock = 40000000,
409 .conf = {
410 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
411 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
412 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
413 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
414 },
415 },
416 {
417 .pixel_clock = 65000000,
418 .conf = {
419 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
420 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
421 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
422 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
423 },
424 },
425 {
426 .pixel_clock = 71000000,
427 .conf = {
428 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
429 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
430 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
431 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
432 },
433 },
434 {
435 .pixel_clock = 73250000,
436 .conf = {
437 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
438 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
439 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
440 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
441 },
442 },
443 {
444 .pixel_clock = 74176000,
445 .conf = {
446 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
447 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
448 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
449 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
450 },
451 },
452 {
453 .pixel_clock = 74250000,
454 .conf = {
455 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
456 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
457 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
458 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
459 },
460 },
461 {
462 .pixel_clock = 83500000,
463 .conf = {
464 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
465 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
466 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
467 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
468 },
469 },
470 {
471 .pixel_clock = 88750000,
472 .conf = {
473 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
474 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
475 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
476 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
477 },
478 },
479 {
480 .pixel_clock = 106500000,
481 .conf = {
482 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
483 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
484 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
485 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
486 },
487 },
488 {
489 .pixel_clock = 108000000,
490 .conf = {
491 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
492 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
493 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
494 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
495 },
496 },
497 {
498 .pixel_clock = 115500000,
499 .conf = {
500 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
501 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
502 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
503 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
504 },
505 },
506 {
507 .pixel_clock = 146250000,
508 .conf = {
509 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
510 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
511 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
512 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
513 },
514 },
515 {
516 .pixel_clock = 148500000,
517 .conf = {
518 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
519 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
520 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
521 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
522 },
523 },
524};
525
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900526static const struct hdmiphy_config hdmiphy_5433_configs[] = {
527 {
528 .pixel_clock = 27000000,
529 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100530 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
531 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
532 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900533 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
534 },
535 },
536 {
537 .pixel_clock = 27027000,
538 .conf = {
539 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100540 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
541 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
542 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900543 },
544 },
545 {
546 .pixel_clock = 40000000,
547 .conf = {
548 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
549 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
550 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
551 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
552 },
553 },
554 {
555 .pixel_clock = 50000000,
556 .conf = {
557 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
558 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
559 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
560 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
561 },
562 },
563 {
564 .pixel_clock = 65000000,
565 .conf = {
566 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
567 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
568 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
569 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
570 },
571 },
572 {
573 .pixel_clock = 74176000,
574 .conf = {
575 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
576 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
577 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
578 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
579 },
580 },
581 {
582 .pixel_clock = 74250000,
583 .conf = {
584 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
585 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
586 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
587 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
588 },
589 },
590 {
591 .pixel_clock = 108000000,
592 .conf = {
593 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
594 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
595 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
596 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
597 },
598 },
599 {
600 .pixel_clock = 148500000,
601 .conf = {
602 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
603 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
604 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
605 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
606 },
607 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100608 {
609 .pixel_clock = 297000000,
610 .conf = {
611 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
612 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
613 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
614 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
615 },
616 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900617};
618
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100619static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900620 "hdmi", "sclk_hdmi"
621};
622
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100623static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900624 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
625};
626
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900627static const char * const hdmi_clk_gates5433[] = {
628 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
629};
630
631static const char * const hdmi_clk_muxes5433[] = {
632 "oscclk", "tmds_clko", "tmds_clko_user",
633 "oscclk", "pixel_clko", "pixel_clko_user"
634};
635
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900636static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
637 .type = HDMI_TYPE13,
638 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_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),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530641};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900642
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100643static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900644 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100645 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900646 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
647 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900648};
649
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900650static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
651 .type = HDMI_TYPE14,
652 .is_apb_phy = 1,
653 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900654 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
655 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200656};
657
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900658static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
659 .type = HDMI_TYPE14,
660 .is_apb_phy = 1,
661 .has_sysreg = 1,
662 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
663 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
664 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
665};
666
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200667static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
668{
669 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
670 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
671 return reg_id;
672}
673
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900674static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
675{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200676 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900677}
678
679static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
680 u32 reg_id, u8 value)
681{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200682 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900683}
684
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200685static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
686 int bytes, u32 val)
687{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200688 reg_id = hdmi_map_reg(hdata, reg_id);
689
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200690 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200691 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200692 val >>= 8;
693 reg_id += 4;
694 }
695}
696
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100697static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
698 u8 *buf, int size)
699{
700 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
701 writel(*buf++, hdata->regs + reg_id);
702}
703
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900704static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
705 u32 reg_id, u32 value, u32 mask)
706{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200707 u32 old;
708
709 reg_id = hdmi_map_reg(hdata, reg_id);
710 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900711 value = (value & mask) | (old & ~mask);
712 writel(value, hdata->regs + reg_id);
713}
714
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900715static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
716 u32 reg_offset, const u8 *buf, u32 len)
717{
718 if ((reg_offset + len) > 32)
719 return -EINVAL;
720
721 if (hdata->hdmiphy_port) {
722 int ret;
723
724 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
725 if (ret == len)
726 return 0;
727 return ret;
728 } else {
729 int i;
730 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200731 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900732 ((reg_offset + i)<<2));
733 return 0;
734 }
735}
736
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900737static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
738{
739 int i, ret;
740
741 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
742 ret = clk_prepare_enable(hdata->clk_gates[i]);
743 if (!ret)
744 continue;
745
746 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
747 hdata->drv_data->clk_gates.data[i], ret);
748 while (i--)
749 clk_disable_unprepare(hdata->clk_gates[i]);
750 return ret;
751 }
752
753 return 0;
754}
755
756static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
757{
758 int i = hdata->drv_data->clk_gates.count;
759
760 while (i--)
761 clk_disable_unprepare(hdata->clk_gates[i]);
762}
763
764static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
765{
766 struct device *dev = hdata->dev;
767 int ret = 0;
768 int i;
769
770 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
771 struct clk **c = &hdata->clk_muxes[i];
772
773 ret = clk_set_parent(c[2], c[to_phy]);
774 if (!ret)
775 continue;
776
777 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
778 hdata->drv_data->clk_muxes.data[i + 2],
779 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
780 }
781
782 return ret;
783}
784
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +0200785static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
786{
787 struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
788 u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
789 int len;
790
791 len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
792 if (len < 0)
793 return len;
794
795 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
796 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
797
798 return 0;
799}
800
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100801static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530802{
Andrzej Hajda991ea042017-09-29 12:05:37 +0200803 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100804 union hdmi_infoframe frm;
805 u8 buf[25];
806 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530807
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530808 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530809 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
810 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100811 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
812 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530813 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
814 return;
815 }
816
Ville Syrjälä13d0add2019-01-08 19:28:25 +0200817 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
818 &hdata->connector, m);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100819 if (!ret)
820 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
821 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530822 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100823 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
824 } else {
825 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
826 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530827
Ville Syrjäläf1781e92017-11-13 19:04:19 +0200828 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
829 &hdata->connector, m);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100830 if (!ret)
831 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
832 sizeof(buf));
833 if (ret > 0) {
834 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100835 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
836 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100837 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530838
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +0200839 hdmi_audio_infoframe_apply(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530840}
841
Sean Pauld9716ee2014-01-30 16:19:29 -0500842static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
843 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500844{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200845 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500846
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200847 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200848 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530849
Hans Verkuil278c8112016-12-13 11:07:17 -0200850 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200851 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500852}
853
Sean Pauld9716ee2014-01-30 16:19:29 -0500854static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900855{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200856 drm_connector_unregister(connector);
857 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900858}
859
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100860static const struct drm_connector_funcs hdmi_connector_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500861 .fill_modes = drm_helper_probe_single_connector_modes,
862 .detect = hdmi_detect,
863 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300864 .reset = drm_atomic_helper_connector_reset,
865 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
866 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500867};
868
869static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200871 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500872 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200873 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900874
Inki Dae8fa04aa2014-03-13 16:38:31 +0900875 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500876 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900877
Inki Dae8fa04aa2014-03-13 16:38:31 +0900878 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500879 if (!edid)
880 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900881
Sean Pauld9716ee2014-01-30 16:19:29 -0500882 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Inki Dae6be90052019-04-15 16:25:12 +0900883 DRM_DEV_DEBUG_KMS(hdata->dev, "%s : width[%d] x height[%d]\n",
884 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
885 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500886
Daniel Vetterc555f022018-07-09 10:40:06 +0200887 drm_connector_update_edid_property(connector, edid);
Hans Verkuil278c8112016-12-13 11:07:17 -0200888 cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
Sean Pauld9716ee2014-01-30 16:19:29 -0500889
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200890 ret = drm_add_edid_modes(connector, edid);
891
892 kfree(edid);
893
894 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900895}
896
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900897static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900898{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100899 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900900 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900901
Andrzej Hajda65e98032015-11-02 14:16:41 +0100902 for (i = 0; i < confs->count; i++)
903 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500904 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500905
Inki Dae6be90052019-04-15 16:25:12 +0900906 DRM_DEV_DEBUG_KMS(hdata->dev, "Could not find phy config for %d\n",
907 pixel_clock);
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500908 return -EINVAL;
909}
910
Sean Pauld9716ee2014-01-30 16:19:29 -0500911static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500912 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900913{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200914 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900915 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900916
Inki Dae6be90052019-04-15 16:25:12 +0900917 DRM_DEV_DEBUG_KMS(hdata->dev,
918 "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
919 mode->hdisplay, mode->vdisplay, mode->vrefresh,
920 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
921 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900922
Rahul Sharma16844fb2013-06-10 14:50:00 +0530923 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900924 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500925 return MODE_BAD;
926
927 return MODE_OK;
928}
929
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100930static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500931 .get_modes = hdmi_get_modes,
932 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500933};
934
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300935static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500936{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300937 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500938 struct drm_connector *connector = &hdata->connector;
939 int ret;
940
Sean Pauld9716ee2014-01-30 16:19:29 -0500941 connector->interlace_allowed = true;
942 connector->polled = DRM_CONNECTOR_POLL_HPD;
943
944 ret = drm_connector_init(hdata->drm_dev, connector,
945 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
946 if (ret) {
Inki Dae6f83d202019-04-15 14:24:36 +0900947 DRM_DEV_ERROR(hdata->dev,
948 "Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900949 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500950 }
951
952 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Daniel Vettercde4c442018-07-09 10:40:07 +0200953 drm_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500954
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100955 if (hdata->bridge) {
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100956 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
957 if (ret)
Inki Dae6f83d202019-04-15 14:24:36 +0900958 DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n");
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100959 }
960
961 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500962}
963
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300964static bool hdmi_mode_fixup(struct drm_encoder *encoder,
965 const struct drm_display_mode *mode,
966 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500967{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300968 struct drm_device *dev = encoder->dev;
969 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500970 struct drm_display_mode *m;
Maciej Purski04fc52f2017-09-05 14:23:02 +0200971 struct drm_connector_list_iter conn_iter;
Sean Paulf041b252014-01-30 16:19:15 -0500972 int mode_ok;
973
Sean Paulf041b252014-01-30 16:19:15 -0500974 drm_mode_set_crtcinfo(adjusted_mode, 0);
975
Maciej Purski04fc52f2017-09-05 14:23:02 +0200976 drm_connector_list_iter_begin(dev, &conn_iter);
977 drm_for_each_connector_iter(connector, &conn_iter) {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300978 if (connector->encoder == encoder)
979 break;
980 }
Maciej Purski04fc52f2017-09-05 14:23:02 +0200981 if (connector)
982 drm_connector_get(connector);
983 drm_connector_list_iter_end(&conn_iter);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300984
Maciej Purski04fc52f2017-09-05 14:23:02 +0200985 if (!connector)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300986 return true;
987
Sean Pauld9716ee2014-01-30 16:19:29 -0500988 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500989
Sean Pauld9716ee2014-01-30 16:19:29 -0500990 if (mode_ok == MODE_OK)
Maciej Purski04fc52f2017-09-05 14:23:02 +0200991 goto cleanup;
Sean Paulf041b252014-01-30 16:19:15 -0500992
993 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900994 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500995 */
996 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500997 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500998
Sean Pauld9716ee2014-01-30 16:19:29 -0500999 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -05001000 DRM_INFO("desired mode doesn't exist so\n");
1001 DRM_INFO("use the most suitable mode among modes.\n");
1002
Inki Dae6be90052019-04-15 16:25:12 +09001003 DRM_DEV_DEBUG_KMS(dev->dev,
1004 "Adjusted Mode: [%d]x[%d] [%d]Hz\n",
1005 m->hdisplay, m->vdisplay,
1006 m->vrefresh);
Sean Paulf041b252014-01-30 16:19:15 -05001007
Sean Paul75626852014-01-30 16:19:16 -05001008 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -05001009 break;
1010 }
1011 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001012
Maciej Purski04fc52f2017-09-05 14:23:02 +02001013cleanup:
1014 drm_connector_put(connector);
1015
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001016 return true;
Sean Paulf041b252014-01-30 16:19:15 -05001017}
1018
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001019static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001020{
1021 u32 n, cts;
1022
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001023 cts = (freq % 9) ? 27000 : 30000;
1024 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001025
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001026 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
1027 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
1028 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001029 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001030}
1031
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001032static void hdmi_audio_config(struct hdmi_context *hdata)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001033{
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001034 u32 bit_ch = 1;
1035 u32 data_num, val;
1036 int i;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001037
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001038 switch (hdata->audio.params.sample_width) {
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001039 case 20:
1040 data_num = 2;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001041 break;
1042 case 24:
1043 data_num = 3;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001044 break;
1045 default:
1046 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001047 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001048 break;
1049 }
1050
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001051 hdmi_reg_acr(hdata, hdata->audio.params.sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001052
1053 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1054 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1055 | HDMI_I2S_MUX_ENABLE);
1056
1057 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1058 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1059
1060 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001061 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1062 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1063
1064 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1065 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1066
1067 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1068 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1069 | HDMI_I2S_SEL_LRCK(6));
Sylwester Nawrockic84b66f2018-02-14 18:23:56 +01001070
1071 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(3)
1072 | HDMI_I2S_SEL_SDATA0(4));
1073
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001074 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1075 | HDMI_I2S_SEL_SDATA2(2));
Sylwester Nawrockic84b66f2018-02-14 18:23:56 +01001076
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001077 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1078
1079 /* I2S_CON_1 & 2 */
1080 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1081 | HDMI_I2S_L_CH_LOW_POL);
1082 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1083 | HDMI_I2S_SET_BIT_CH(bit_ch)
1084 | HDMI_I2S_SET_SDATA_BIT(data_num)
1085 | HDMI_I2S_BASIC_FORMAT);
1086
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001087 /* Configuration of the audio channel status registers */
1088 for (i = 0; i < HDMI_I2S_CH_ST_MAXNUM; i++)
1089 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST(i),
1090 hdata->audio.params.iec.status[i]);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001091
1092 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1093}
1094
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001095static void hdmi_audio_control(struct hdmi_context *hdata)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001096{
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001097 bool enable = !hdata->audio.mute;
1098
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001099 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001100 return;
1101
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001102 hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ?
1103 HDMI_AVI_CON_EVERY_VSYNC : HDMI_AUI_CON_NO_TRAN);
1104 hdmi_reg_writemask(hdata, HDMI_CON_0, enable ?
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001105 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1106}
1107
Rahul Sharmabfa48422014-04-03 20:41:04 +05301108static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001109{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001110 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Rahul Sharmabfa48422014-04-03 20:41:04 +05301111 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001112
Andrzej Hajda991ea042017-09-29 12:05:37 +02001113 if (m->flags & DRM_MODE_FLAG_INTERLACE)
Rahul Sharmabfa48422014-04-03 20:41:04 +05301114 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001115
Rahul Sharmabfa48422014-04-03 20:41:04 +05301116 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1117 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001118}
1119
1120static void hdmi_conf_init(struct hdmi_context *hdata)
1121{
Sean Paul77006a72013-01-16 10:17:20 -05001122 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001123 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1124 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001125
1126 /* choose HDMI mode */
1127 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1128 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001129 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301130 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001131 /* disable bluescreen */
1132 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001133
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001134 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001135 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1136 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1137 hdmi_reg_writeb(hdata, HDMI_CON_2,
1138 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1139 }
1140
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001141 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001142 /* choose bluescreen (fecal) color */
1143 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1144 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1145 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1146
1147 /* enable AVI packet every vsync, fixes purple line problem */
1148 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1149 /* force RGB, look to CEA-861-D, table 7 for more detail */
1150 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1151 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1152
1153 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1154 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1155 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1156 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001157 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301158
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001159 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001160 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1161 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001162}
1163
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001164static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1165{
1166 int tries;
1167
1168 for (tries = 0; tries < 10; ++tries) {
1169 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1170
1171 if (val & HDMI_PHY_STATUS_READY) {
Inki Dae6be90052019-04-15 16:25:12 +09001172 DRM_DEV_DEBUG_KMS(hdata->dev,
1173 "PLL stabilized after %d tries\n",
1174 tries);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001175 return;
1176 }
1177 usleep_range(10, 20);
1178 }
1179
Inki Dae6f83d202019-04-15 14:24:36 +09001180 DRM_DEV_ERROR(hdata->dev, "PLL could not reach steady state\n");
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001181}
1182
Rahul Sharma16844fb2013-06-10 14:50:00 +05301183static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001184{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001185 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001186 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001187
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001188 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1189 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1190 (m->htotal << 12) | m->vtotal);
1191
1192 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1193 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1194
1195 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1196 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1197
1198 val = (m->hsync_start - m->hdisplay - 2);
1199 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001200 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001201 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1202
1203 /*
1204 * Quirk requirement for exynos HDMI IP design,
1205 * 2 pixels less than the actual calculation for hsync_start
1206 * and end.
1207 */
1208
1209 /* Following values & calculations differ for different type of modes */
1210 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001211 val = ((m->vsync_end - m->vdisplay) / 2);
1212 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1213 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1214
1215 val = m->vtotal / 2;
1216 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1217 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1218
1219 val = (m->vtotal +
1220 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1221 val |= m->vtotal << 11;
1222 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1223
1224 val = ((m->vtotal / 2) + 7);
1225 val |= ((m->vtotal / 2) + 2) << 12;
1226 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1227
1228 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1229 val |= ((m->htotal / 2) +
1230 (m->hsync_start - m->hdisplay)) << 12;
1231 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1232
1233 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1234 (m->vtotal - m->vdisplay) / 2);
1235 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1236
1237 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1238 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001239 val = m->vtotal;
1240 val |= (m->vtotal - m->vdisplay) << 11;
1241 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1242
1243 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1244
1245 val = (m->vsync_end - m->vdisplay);
1246 val |= ((m->vsync_start - m->vdisplay) << 12);
1247 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1248
1249 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1250 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1251 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1252 m->vtotal - m->vdisplay);
1253 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001254 }
1255
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001256 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1257 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1258 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1259 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001260}
1261
Rahul Sharma16844fb2013-06-10 14:50:00 +05301262static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001263{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001264 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajda36fd8872017-09-29 12:05:40 +02001265 struct drm_display_mode *am =
1266 &hdata->encoder.crtc->state->adjusted_mode;
1267 int hquirk = 0;
1268
1269 /*
1270 * In case video mode coming from CRTC differs from requested one HDMI
1271 * sometimes is able to almost properly perform conversion - only
1272 * first line is distorted.
1273 */
1274 if ((m->vdisplay != am->vdisplay) &&
Daniel Drakeae58c032017-09-29 12:05:42 +02001275 (m->hdisplay == 1280 || m->hdisplay == 1024 || m->hdisplay == 1366))
Andrzej Hajda36fd8872017-09-29 12:05:40 +02001276 hquirk = 258;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001277
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001278 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1279 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1280 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1281 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001282 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001283 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1284 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1285 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1286 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1287
1288 /*
1289 * Quirk requirement for exynos 5 HDMI IP design,
1290 * 2 pixels less than the actual calculation for hsync_start
1291 * and end.
1292 */
1293
1294 /* Following values & calculations differ for different type of modes */
1295 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001296 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1297 (m->vsync_end - m->vdisplay) / 2);
1298 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1299 (m->vsync_start - m->vdisplay) / 2);
1300 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1301 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1302 (m->vtotal - m->vdisplay) / 2);
1303 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1304 m->vtotal - m->vdisplay / 2);
1305 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1306 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1307 (m->vtotal / 2) + 7);
1308 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1309 (m->vtotal / 2) + 2);
1310 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1311 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1312 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1313 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1314 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1315 (m->vtotal - m->vdisplay) / 2);
1316 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1317 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1318 m->vtotal - m->vdisplay / 2);
1319 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1320 (m->vtotal / 2) + 1);
1321 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1322 (m->vtotal / 2) + 1);
1323 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1324 (m->vtotal / 2) + 1);
1325 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1326 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1327 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001328 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1329 m->vsync_end - m->vdisplay);
1330 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1331 m->vsync_start - m->vdisplay);
1332 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1333 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1334 m->vtotal - m->vdisplay);
1335 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1336 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1337 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1338 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1339 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1340 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1341 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1342 m->vtotal - m->vdisplay);
1343 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001344 }
1345
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001346 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1347 m->hsync_start - m->hdisplay - 2);
1348 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1349 m->hsync_end - m->hdisplay - 2);
1350 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1351 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1352 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1353 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1354 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1355 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1356 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1357 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1358 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1359 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1360 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1361 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1362 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1363 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1364 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1365 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1366 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1367 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001368
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001369 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
Andrzej Hajda36fd8872017-09-29 12:05:40 +02001370 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2,
1371 m->htotal - m->hdisplay - hquirk);
1372 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay + hquirk);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001373 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001374 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1375 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001376}
1377
Rahul Sharma16844fb2013-06-10 14:50:00 +05301378static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001379{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001380 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301381 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001382 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301383 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001384
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001385 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001386}
1387
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001388static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1389{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001390 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1391 usleep_range(10000, 12000);
1392 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1393 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001394 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001395 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001396 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001397 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001398}
1399
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001400static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1401{
1402 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1403
1404 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1405 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1406}
1407
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001408static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1409{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001410 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001411 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001412 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001413
Andrzej Hajda991ea042017-09-29 12:05:37 +02001414 ret = hdmi_find_phy_conf(hdata, m->clock * 1000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001415 if (ret < 0) {
Inki Dae6f83d202019-04-15 14:24:36 +09001416 DRM_DEV_ERROR(hdata->dev, "failed to find hdmiphy conf\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001417 return;
1418 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001419 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1420
1421 hdmi_clk_set_parents(hdata, false);
1422
1423 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001424
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001425 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001426 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001427 if (ret) {
Inki Dae6f83d202019-04-15 14:24:36 +09001428 DRM_DEV_ERROR(hdata->dev, "failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001429 return;
1430 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001431 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001432 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001433 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001434 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001435}
1436
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001437/* Should be called with hdata->mutex mutex held */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001438static void hdmi_conf_apply(struct hdmi_context *hdata)
1439{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301440 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001441 hdmi_conf_init(hdata);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001442 hdmi_audio_config(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301443 hdmi_mode_apply(hdata);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001444 hdmi_audio_control(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001445}
1446
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001447static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1448{
1449 if (!hdata->sysreg)
1450 return;
1451
1452 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1453 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1454}
1455
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001456/* Should be called with hdata->mutex mutex held. */
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001457static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001458{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001459 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001460 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001461
Sean Paulaf65c802014-01-30 16:19:27 -05001462 pm_runtime_get_sync(hdata->dev);
1463
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001464 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Inki Dae6be90052019-04-15 16:25:12 +09001465 DRM_DEV_DEBUG_KMS(hdata->dev,
1466 "failed to enable regulator bulk\n");
Seung-Woo Kimad079452013-06-05 14:34:38 +09001467
Rahul Sharma049d34e2014-05-20 10:36:05 +05301468 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1469 PMU_HDMI_PHY_ENABLE_BIT, 1);
1470
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001471 hdmi_set_refclk(hdata, true);
1472
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001473 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1474
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001475 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001476
1477 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001478}
1479
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001480/* Should be called with hdata->mutex mutex held. */
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001481static void hdmiphy_disable(struct hdmi_context *hdata)
1482{
1483 if (!hdata->powered)
1484 return;
1485
1486 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1487
1488 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1489
1490 hdmi_set_refclk(hdata, false);
1491
1492 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1493 PMU_HDMI_PHY_ENABLE_BIT, 0);
1494
1495 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1496
1497 pm_runtime_put_sync(hdata->dev);
1498
1499 hdata->powered = false;
1500}
1501
1502static void hdmi_enable(struct drm_encoder *encoder)
1503{
1504 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1505
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001506 mutex_lock(&hdata->mutex);
1507
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001508 hdmiphy_enable(hdata);
1509 hdmi_conf_apply(hdata);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001510
1511 mutex_unlock(&hdata->mutex);
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001512}
1513
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001514static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001515{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001516 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001517
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001518 mutex_lock(&hdata->mutex);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001519
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001520 if (hdata->powered) {
1521 /*
1522 * The SFRs of VP and Mixer are updated by Vertical Sync of
1523 * Timing generator which is a part of HDMI so the sequence
1524 * to disable TV Subsystem should be as following,
1525 * VP -> Mixer -> HDMI
1526 *
1527 * To achieve such sequence HDMI is disabled together with
1528 * HDMI PHY, via pipe clock callback.
1529 */
1530 mutex_unlock(&hdata->mutex);
1531 cancel_delayed_work(&hdata->hotplug_work);
1532 cec_notifier_set_phys_addr(hdata->notifier,
1533 CEC_PHYS_ADDR_INVALID);
1534 return;
1535 }
1536
1537 mutex_unlock(&hdata->mutex);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001538}
1539
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001540static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001541 .mode_fixup = hdmi_mode_fixup,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001542 .enable = hdmi_enable,
1543 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001544};
1545
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001546static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001547 .destroy = drm_encoder_cleanup,
1548};
1549
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001550static void hdmi_audio_shutdown(struct device *dev, void *data)
1551{
1552 struct hdmi_context *hdata = dev_get_drvdata(dev);
1553
1554 mutex_lock(&hdata->mutex);
1555
1556 hdata->audio.mute = true;
1557
1558 if (hdata->powered)
1559 hdmi_audio_control(hdata);
1560
1561 mutex_unlock(&hdata->mutex);
1562}
1563
1564static int hdmi_audio_hw_params(struct device *dev, void *data,
1565 struct hdmi_codec_daifmt *daifmt,
1566 struct hdmi_codec_params *params)
1567{
1568 struct hdmi_context *hdata = dev_get_drvdata(dev);
1569
1570 if (daifmt->fmt != HDMI_I2S || daifmt->bit_clk_inv ||
1571 daifmt->frame_clk_inv || daifmt->bit_clk_master ||
1572 daifmt->frame_clk_master) {
1573 dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
1574 daifmt->bit_clk_inv, daifmt->frame_clk_inv,
1575 daifmt->bit_clk_master,
1576 daifmt->frame_clk_master);
1577 return -EINVAL;
1578 }
1579
1580 mutex_lock(&hdata->mutex);
1581
1582 hdata->audio.params = *params;
1583
1584 if (hdata->powered) {
1585 hdmi_audio_config(hdata);
1586 hdmi_audio_infoframe_apply(hdata);
1587 }
1588
1589 mutex_unlock(&hdata->mutex);
1590
1591 return 0;
1592}
1593
1594static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute)
1595{
1596 struct hdmi_context *hdata = dev_get_drvdata(dev);
1597
1598 mutex_lock(&hdata->mutex);
1599
1600 hdata->audio.mute = mute;
1601
1602 if (hdata->powered)
1603 hdmi_audio_control(hdata);
1604
1605 mutex_unlock(&hdata->mutex);
1606
1607 return 0;
1608}
1609
1610static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
1611 size_t len)
1612{
1613 struct hdmi_context *hdata = dev_get_drvdata(dev);
1614 struct drm_connector *connector = &hdata->connector;
1615
1616 memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
1617
1618 return 0;
1619}
1620
1621static const struct hdmi_codec_ops audio_codec_ops = {
1622 .hw_params = hdmi_audio_hw_params,
1623 .audio_shutdown = hdmi_audio_shutdown,
1624 .digital_mute = hdmi_audio_digital_mute,
1625 .get_eld = hdmi_audio_get_eld,
1626};
1627
1628static int hdmi_register_audio_device(struct hdmi_context *hdata)
1629{
1630 struct hdmi_codec_pdata codec_data = {
1631 .ops = &audio_codec_ops,
1632 .max_i2s_channels = 6,
1633 .i2s = 1,
1634 };
1635
1636 hdata->audio.pdev = platform_device_register_data(
1637 hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
1638 &codec_data, sizeof(codec_data));
1639
1640 return PTR_ERR_OR_ZERO(hdata->audio.pdev);
1641}
1642
Sean Paul724fd142014-05-09 15:05:10 +09001643static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001644{
Sean Paul724fd142014-05-09 15:05:10 +09001645 struct hdmi_context *hdata;
1646
1647 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001648
Sean Paul45517892014-01-30 16:19:05 -05001649 if (hdata->drm_dev)
1650 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001651}
1652
1653static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1654{
1655 struct hdmi_context *hdata = arg;
1656
1657 mod_delayed_work(system_wq, &hdata->hotplug_work,
1658 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001659
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001660 return IRQ_HANDLED;
1661}
1662
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001663static int hdmi_clks_get(struct hdmi_context *hdata,
1664 const struct string_array_spec *names,
1665 struct clk **clks)
1666{
1667 struct device *dev = hdata->dev;
1668 int i;
1669
1670 for (i = 0; i < names->count; ++i) {
1671 struct clk *clk = devm_clk_get(dev, names->data[i]);
1672
1673 if (IS_ERR(clk)) {
1674 int ret = PTR_ERR(clk);
1675
1676 dev_err(dev, "Cannot get clock %s, %d\n",
1677 names->data[i], ret);
1678
1679 return ret;
1680 }
1681
1682 clks[i] = clk;
1683 }
1684
1685 return 0;
1686}
1687
1688static int hdmi_clk_init(struct hdmi_context *hdata)
1689{
1690 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1691 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1692 struct device *dev = hdata->dev;
1693 struct clk **clks;
1694 int ret;
1695
1696 if (!count)
1697 return 0;
1698
Kees Cooka86854d2018-06-12 14:07:58 -07001699 clks = devm_kcalloc(dev, count, sizeof(*clks), GFP_KERNEL);
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001700 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001701 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001702
1703 hdata->clk_gates = clks;
1704 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1705
1706 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1707 if (ret)
1708 return ret;
1709
1710 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1711}
1712
1713
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001714static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1715{
1716 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1717 phy_clk);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001718 mutex_lock(&hdata->mutex);
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001719
1720 if (enable)
1721 hdmiphy_enable(hdata);
1722 else
1723 hdmiphy_disable(hdata);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001724
1725 mutex_unlock(&hdata->mutex);
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001726}
1727
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001728static int hdmi_bridge_init(struct hdmi_context *hdata)
1729{
1730 struct device *dev = hdata->dev;
1731 struct device_node *ep, *np;
1732
1733 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1734 if (!ep)
1735 return 0;
1736
1737 np = of_graph_get_remote_port_parent(ep);
1738 of_node_put(ep);
1739 if (!np) {
Inki Dae6f83d202019-04-15 14:24:36 +09001740 DRM_DEV_ERROR(dev, "failed to get remote port parent");
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001741 return -EINVAL;
1742 }
1743
1744 hdata->bridge = of_drm_find_bridge(np);
1745 of_node_put(np);
1746
1747 if (!hdata->bridge)
1748 return -EPROBE_DEFER;
1749
1750 return 0;
1751}
1752
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001753static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001754{
1755 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001756 int i, ret;
1757
Inki Dae6be90052019-04-15 16:25:12 +09001758 DRM_DEV_DEBUG_KMS(dev, "HDMI resource init\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001759
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001760 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1761 if (IS_ERR(hdata->hpd_gpio)) {
Inki Dae6f83d202019-04-15 14:24:36 +09001762 DRM_DEV_ERROR(dev, "cannot get hpd gpio property\n");
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001763 return PTR_ERR(hdata->hpd_gpio);
1764 }
1765
1766 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1767 if (hdata->irq < 0) {
Inki Dae6f83d202019-04-15 14:24:36 +09001768 DRM_DEV_ERROR(dev, "failed to get GPIO irq\n");
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001769 return hdata->irq;
1770 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001771
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001772 ret = hdmi_clk_init(hdata);
1773 if (ret)
1774 return ret;
1775
1776 ret = hdmi_clk_set_parents(hdata, false);
1777 if (ret)
1778 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001779
Milo Kimc0d656d2016-08-31 15:14:27 +09001780 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001781 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001782
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001783 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001784 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001785 if (ret != -EPROBE_DEFER)
Inki Dae6f83d202019-04-15 14:24:36 +09001786 DRM_DEV_ERROR(dev, "failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001787 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001788 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001789
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001790 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001791
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001792 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1793 if (IS_ERR(hdata->reg_hdmi_en))
1794 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001795
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001796 ret = regulator_enable(hdata->reg_hdmi_en);
1797 if (ret) {
Inki Dae6f83d202019-04-15 14:24:36 +09001798 DRM_DEV_ERROR(dev,
1799 "failed to enable hdmi-en regulator\n");
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001800 return ret;
1801 }
1802 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001803
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001804 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001805}
1806
Arvind Yadave3cc51e2017-06-19 14:54:03 +05301807static const struct of_device_id hdmi_match_types[] = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301808 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001809 .compatible = "samsung,exynos4210-hdmi",
1810 .data = &exynos4210_hdmi_driver_data,
1811 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301812 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001813 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301814 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301815 .compatible = "samsung,exynos5420-hdmi",
1816 .data = &exynos5420_hdmi_driver_data,
1817 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001818 .compatible = "samsung,exynos5433-hdmi",
1819 .data = &exynos5433_hdmi_driver_data,
1820 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301821 /* end node */
1822 }
1823};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001824MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301825
Inki Daef37cd5e2014-05-09 14:25:20 +09001826static int hdmi_bind(struct device *dev, struct device *master, void *data)
1827{
1828 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001829 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001830 struct drm_encoder *encoder = &hdata->encoder;
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001831 struct exynos_drm_crtc *crtc;
1832 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001833
Inki Daef37cd5e2014-05-09 14:25:20 +09001834 hdata->drm_dev = drm_dev;
1835
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001836 hdata->phy_clk.enable = hdmiphy_clk_enable;
1837
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001838 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001839 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001840
1841 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1842
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001843 ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_HDMI);
1844 if (ret < 0)
1845 return ret;
1846
1847 crtc = exynos_drm_crtc_get_by_type(drm_dev, EXYNOS_DISPLAY_TYPE_HDMI);
1848 crtc->pipe_clk = &hdata->phy_clk;
1849
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001850 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001851 if (ret) {
Inki Dae6f83d202019-04-15 14:24:36 +09001852 DRM_DEV_ERROR(dev, "failed to create connector ret = %d\n",
1853 ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001854 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001855 return ret;
1856 }
1857
1858 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001859}
1860
1861static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1862{
Inki Daef37cd5e2014-05-09 14:25:20 +09001863}
1864
1865static const struct component_ops hdmi_component_ops = {
1866 .bind = hdmi_bind,
1867 .unbind = hdmi_unbind,
1868};
1869
Milo Kim1caa3602016-08-31 15:14:25 +09001870static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001871{
1872 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1873 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001874 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001875
1876 np = of_find_compatible_node(NULL, NULL, compatible_str);
1877 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001878 np = of_get_next_parent(np);
1879 else
1880 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001881
Milo Kim1caa3602016-08-31 15:14:25 +09001882 if (!np) {
Inki Dae6f83d202019-04-15 14:24:36 +09001883 DRM_DEV_ERROR(hdata->dev,
1884 "Failed to find ddc node in device tree\n");
Milo Kim1caa3602016-08-31 15:14:25 +09001885 return -ENODEV;
1886 }
1887
1888 adpt = of_find_i2c_adapter_by_node(np);
1889 of_node_put(np);
1890
1891 if (!adpt) {
1892 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1893 return -EPROBE_DEFER;
1894 }
1895
1896 hdata->ddc_adpt = adpt;
1897
1898 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001899}
1900
Milo Kimb5413022016-08-31 15:14:26 +09001901static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001902{
1903 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001904 struct device_node *np;
1905 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001906
Milo Kimb5413022016-08-31 15:14:26 +09001907 np = of_find_compatible_node(NULL, NULL, compatible_str);
1908 if (!np) {
1909 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1910 if (!np) {
Inki Dae6f83d202019-04-15 14:24:36 +09001911 DRM_DEV_ERROR(hdata->dev,
1912 "Failed to find hdmiphy node in device tree\n");
Milo Kimb5413022016-08-31 15:14:26 +09001913 return -ENODEV;
1914 }
1915 }
1916
1917 if (hdata->drv_data->is_apb_phy) {
1918 hdata->regs_hdmiphy = of_iomap(np, 0);
1919 if (!hdata->regs_hdmiphy) {
Inki Dae6f83d202019-04-15 14:24:36 +09001920 DRM_DEV_ERROR(hdata->dev,
1921 "failed to ioremap hdmi phy\n");
Milo Kimb5413022016-08-31 15:14:26 +09001922 ret = -ENOMEM;
1923 goto out;
1924 }
1925 } else {
1926 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1927 if (!hdata->hdmiphy_port) {
1928 DRM_INFO("Failed to get hdmi phy i2c client\n");
1929 ret = -EPROBE_DEFER;
1930 goto out;
1931 }
1932 }
1933
1934out:
1935 of_node_put(np);
1936 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001937}
1938
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001939static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001940{
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001941 struct hdmi_audio_infoframe *audio_infoframe;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001942 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001943 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001944 struct resource *res;
1945 int ret;
1946
Andrzej Hajda930865f2014-11-17 09:54:20 +01001947 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1948 if (!hdata)
1949 return -ENOMEM;
1950
Marek Szyprowski57a64122016-04-01 15:17:44 +02001951 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001952
Andrzej Hajda930865f2014-11-17 09:54:20 +01001953 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001954
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001955 hdata->dev = dev;
1956
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02001957 mutex_init(&hdata->mutex);
1958
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001959 ret = hdmi_resources_init(hdata);
1960 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001961 if (ret != -EPROBE_DEFER)
Inki Dae6f83d202019-04-15 14:24:36 +09001962 DRM_DEV_ERROR(dev, "hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001963 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001964 }
1965
1966 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001967 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001968 if (IS_ERR(hdata->regs)) {
1969 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001970 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001971 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001972
Milo Kim1caa3602016-08-31 15:14:25 +09001973 ret = hdmi_get_ddc_adapter(hdata);
1974 if (ret)
1975 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001976
Milo Kimb5413022016-08-31 15:14:26 +09001977 ret = hdmi_get_phy_io(hdata);
1978 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001979 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001980
Sean Paul724fd142014-05-09 15:05:10 +09001981 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1982
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001983 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001984 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001985 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001986 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001987 if (ret) {
Inki Dae6f83d202019-04-15 14:24:36 +09001988 DRM_DEV_ERROR(dev, "failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001989 goto err_hdmiphy;
1990 }
1991
Rahul Sharma049d34e2014-05-20 10:36:05 +05301992 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1993 "samsung,syscon-phandle");
1994 if (IS_ERR(hdata->pmureg)) {
Inki Dae6f83d202019-04-15 14:24:36 +09001995 DRM_DEV_ERROR(dev, "syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001996 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301997 goto err_hdmiphy;
1998 }
1999
Andrzej Hajda68cd0042016-01-14 14:40:07 +09002000 if (hdata->drv_data->has_sysreg) {
2001 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
2002 "samsung,sysreg-phandle");
2003 if (IS_ERR(hdata->sysreg)) {
Inki Dae6f83d202019-04-15 14:24:36 +09002004 DRM_DEV_ERROR(dev, "sysreg regmap lookup failed.\n");
Andrzej Hajda68cd0042016-01-14 14:40:07 +09002005 ret = -EPROBE_DEFER;
2006 goto err_hdmiphy;
2007 }
2008 }
2009
Hans Verkuil278c8112016-12-13 11:07:17 -02002010 hdata->notifier = cec_notifier_get(&pdev->dev);
2011 if (hdata->notifier == NULL) {
2012 ret = -ENOMEM;
2013 goto err_hdmiphy;
2014 }
2015
Sean Paulaf65c802014-01-30 16:19:27 -05002016 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002017
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02002018 audio_infoframe = &hdata->audio.infoframe;
2019 hdmi_audio_infoframe_init(audio_infoframe);
2020 audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
2021 audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
2022 audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
2023 audio_infoframe->channels = 2;
2024
2025 ret = hdmi_register_audio_device(hdata);
Inki Daedf5225b2014-05-29 18:28:02 +09002026 if (ret)
Hans Verkuil278c8112016-12-13 11:07:17 -02002027 goto err_notifier_put;
Inki Daedf5225b2014-05-29 18:28:02 +09002028
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02002029 ret = component_add(&pdev->dev, &hdmi_component_ops);
2030 if (ret)
2031 goto err_unregister_audio;
2032
Inki Daedf5225b2014-05-29 18:28:02 +09002033 return ret;
2034
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02002035err_unregister_audio:
2036 platform_device_unregister(hdata->audio.pdev);
2037
Hans Verkuil278c8112016-12-13 11:07:17 -02002038err_notifier_put:
2039 cec_notifier_put(hdata->notifier);
Inki Daedf5225b2014-05-29 18:28:02 +09002040 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002041
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002042err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09002043 if (hdata->hdmiphy_port)
2044 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05302045 if (hdata->regs_hdmiphy)
2046 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002047err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09002048 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09002049
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002050 return ret;
2051}
2052
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08002053static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002054{
Andrzej Hajda930865f2014-11-17 09:54:20 +01002055 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002056
Sean Paul724fd142014-05-09 15:05:10 +09002057 cancel_delayed_work_sync(&hdata->hotplug_work);
Hans Verkuil278c8112016-12-13 11:07:17 -02002058 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09002059
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02002060 component_del(&pdev->dev, &hdmi_component_ops);
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02002061 platform_device_unregister(hdata->audio.pdev);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02002062
Hans Verkuil278c8112016-12-13 11:07:17 -02002063 cec_notifier_put(hdata->notifier);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02002064 pm_runtime_disable(&pdev->dev);
2065
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02002066 if (!IS_ERR(hdata->reg_hdmi_en))
2067 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02002068
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09002069 if (hdata->hdmiphy_port)
2070 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09002071
Arvind Yadavd7420002016-10-19 15:34:16 +05302072 if (hdata->regs_hdmiphy)
2073 iounmap(hdata->regs_hdmiphy);
2074
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02002075 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002076
Sylwester Nawrocki691da76f2017-10-23 14:49:34 +02002077 mutex_destroy(&hdata->mutex);
2078
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002079 return 0;
2080}
2081
Arnd Bergmann7e175102017-07-21 22:47:18 +02002082static int __maybe_unused exynos_hdmi_suspend(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002083{
2084 struct hdmi_context *hdata = dev_get_drvdata(dev);
2085
Andrzej Hajda9be7e982016-01-14 14:22:47 +09002086 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002087
2088 return 0;
2089}
2090
Arnd Bergmann7e175102017-07-21 22:47:18 +02002091static int __maybe_unused exynos_hdmi_resume(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002092{
2093 struct hdmi_context *hdata = dev_get_drvdata(dev);
2094 int ret;
2095
Andrzej Hajda9be7e982016-01-14 14:22:47 +09002096 ret = hdmi_clk_enable_gates(hdata);
2097 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002098 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002099
2100 return 0;
2101}
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002102
2103static const struct dev_pm_ops exynos_hdmi_pm_ops = {
2104 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
Marek Szyprowski7e915742018-06-11 14:25:00 +02002105 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
2106 pm_runtime_force_resume)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002107};
2108
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002109struct platform_driver hdmi_driver = {
2110 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08002111 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002112 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05302113 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002114 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09002115 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05302116 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09002117 },
2118};