blob: 218914339cacbde226600027a915b30913056da7 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090010 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
Seung-Woo Kimd8408322011-12-21 17:39:39 +090013 * option) any later version.
14 *
15 */
16
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
18#include <drm/drm_edid.h>
19#include <drm/drm_crtc_helper.h>
Gustavo Padovan4ea95262015-06-01 12:04:44 -030020#include <drm/drm_atomic_helper.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090021
22#include "regs-hdmi.h"
23
24#include <linux/kernel.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090025#include <linux/wait.h>
26#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090027#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
Andrzej Hajda2228b7c2015-09-25 14:48:24 +020033#include <linux/gpio/consumer.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090034#include <linux/regulator/consumer.h>
Rahul Sharma22c4f422012-10-04 20:48:55 +053035#include <linux/io.h>
Rahul Sharmad5e9ca42014-05-09 15:34:18 +090036#include <linux/of_address.h>
Andrzej Hajdacd240cd2015-07-09 16:28:09 +020037#include <linux/of_device.h>
Andrzej Hajdaaa181572017-02-01 09:29:14 +010038#include <linux/of_graph.h>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053039#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090040#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053041#include <linux/mfd/syscon.h>
42#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090043
44#include <drm/exynos_drm.h>
45
Hans Verkuil278c8112016-12-13 11:07:17 -020046#include <media/cec-notifier.h>
47
Inki Daef37cd5e2014-05-09 14:25:20 +090048#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090049
Sean Paul724fd142014-05-09 15:05:10 +090050#define HOTPLUG_DEBOUNCE_MS 1100
51
Rahul Sharma5a325072012-10-04 20:48:54 +053052enum hdmi_type {
53 HDMI_TYPE13,
54 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020055 HDMI_TYPE_COUNT
56};
57
58#define HDMI_MAPPED_BASE 0xffff0000
59
60enum hdmi_mapped_regs {
61 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
62 HDMI_PHY_RSTOUT,
63 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020064 HDMI_ACR_MCTS0,
65 HDMI_ACR_CTS0,
66 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020067};
68
69static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
70 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
71 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
72 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020073 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
74 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
75 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053076};
77
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020078static const char * const supply[] = {
79 "vdd",
80 "vdd_osc",
81 "vdd_pll",
82};
83
Andrzej Hajda65e98032015-11-02 14:16:41 +010084struct hdmiphy_config {
85 int pixel_clock;
86 u8 conf[32];
87};
88
89struct hdmiphy_configs {
90 int count;
91 const struct hdmiphy_config *data;
92};
93
Andrzej Hajda9be7e982016-01-14 14:22:47 +090094struct string_array_spec {
95 int count;
96 const char * const *data;
97};
98
99#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
100
Inki Daebfe4e842014-03-06 14:18:17 +0900101struct hdmi_driver_data {
102 unsigned int type;
103 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900104 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100105 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900106 struct string_array_spec clk_gates;
107 /*
108 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
109 * required parents of clock when HDMI-PHY is respectively off or on.
110 */
111 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900112};
113
Joonyoung Shim590f4182012-03-16 18:47:14 +0900114struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300115 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900116 struct device *dev;
117 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500118 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900119 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900120 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900121 struct delayed_work hotplug_work;
Hans Verkuil278c8112016-12-13 11:07:17 -0200122 struct cec_notifier *notifier;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200123 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900124
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200125 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900126 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200127 struct i2c_client *hdmiphy_port;
128 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900129 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200130 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530131 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900132 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900133 struct clk **clk_gates;
134 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200135 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
136 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900137 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100138 struct drm_bridge *bridge;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900139};
140
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300141static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100142{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900143 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100144}
145
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200146static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
147{
148 return container_of(c, struct hdmi_context, connector);
149}
150
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900151static const struct hdmiphy_config hdmiphy_v13_configs[] = {
152 {
153 .pixel_clock = 27000000,
154 .conf = {
155 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
156 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
157 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200158 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900159 },
160 },
161 {
162 .pixel_clock = 27027000,
163 .conf = {
164 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
165 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
166 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200167 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900168 },
169 },
170 {
171 .pixel_clock = 74176000,
172 .conf = {
173 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
174 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
175 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200176 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900177 },
178 },
179 {
180 .pixel_clock = 74250000,
181 .conf = {
182 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
183 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
184 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200185 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900186 },
187 },
188 {
189 .pixel_clock = 148500000,
190 .conf = {
191 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
192 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
193 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200194 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900195 },
196 },
197};
198
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500199static const struct hdmiphy_config hdmiphy_v14_configs[] = {
200 {
201 .pixel_clock = 25200000,
202 .conf = {
203 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
204 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
205 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
206 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
207 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900208 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500209 {
210 .pixel_clock = 27000000,
211 .conf = {
212 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
213 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
214 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
215 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
216 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900217 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500218 {
219 .pixel_clock = 27027000,
220 .conf = {
221 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
222 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
223 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200224 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500225 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900226 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500227 {
228 .pixel_clock = 36000000,
229 .conf = {
230 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
231 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
232 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
233 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
234 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900235 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500236 {
237 .pixel_clock = 40000000,
238 .conf = {
239 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
240 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
241 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
242 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
243 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900244 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500245 {
246 .pixel_clock = 65000000,
247 .conf = {
248 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
249 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
250 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
251 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
252 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900253 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500254 {
Shirish Se1d883c2014-03-13 14:28:27 +0900255 .pixel_clock = 71000000,
256 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530257 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
258 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
259 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900260 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
261 },
262 },
263 {
264 .pixel_clock = 73250000,
265 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530266 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
267 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
268 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900269 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
270 },
271 },
272 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500273 .pixel_clock = 74176000,
274 .conf = {
275 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
276 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
277 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
278 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
279 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900280 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500281 {
282 .pixel_clock = 74250000,
283 .conf = {
284 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
285 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
286 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200287 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500288 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900289 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500290 {
291 .pixel_clock = 83500000,
292 .conf = {
293 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
294 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
295 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
296 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
297 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900298 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500299 {
300 .pixel_clock = 106500000,
301 .conf = {
302 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
303 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
304 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
305 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
306 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900307 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500308 {
309 .pixel_clock = 108000000,
310 .conf = {
311 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
312 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
313 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
314 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
315 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900316 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500317 {
Shirish Se1d883c2014-03-13 14:28:27 +0900318 .pixel_clock = 115500000,
319 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530320 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
321 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
322 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900323 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
324 },
325 },
326 {
327 .pixel_clock = 119000000,
328 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530329 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
330 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
331 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900332 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
333 },
334 },
335 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500336 .pixel_clock = 146250000,
337 .conf = {
338 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
339 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
340 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
341 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
342 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900343 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500344 {
345 .pixel_clock = 148500000,
346 .conf = {
347 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
348 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
349 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200350 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500351 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900352 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900353};
354
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530355static const struct hdmiphy_config hdmiphy_5420_configs[] = {
356 {
357 .pixel_clock = 25200000,
358 .conf = {
359 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
360 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
361 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
362 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
363 },
364 },
365 {
366 .pixel_clock = 27000000,
367 .conf = {
368 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
369 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
370 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
371 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
372 },
373 },
374 {
375 .pixel_clock = 27027000,
376 .conf = {
377 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
378 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
379 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
380 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
381 },
382 },
383 {
384 .pixel_clock = 36000000,
385 .conf = {
386 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
387 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
388 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
389 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
390 },
391 },
392 {
393 .pixel_clock = 40000000,
394 .conf = {
395 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
396 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
397 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
398 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
399 },
400 },
401 {
402 .pixel_clock = 65000000,
403 .conf = {
404 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
405 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
406 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
407 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
408 },
409 },
410 {
411 .pixel_clock = 71000000,
412 .conf = {
413 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
414 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
415 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
416 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
417 },
418 },
419 {
420 .pixel_clock = 73250000,
421 .conf = {
422 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
423 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
424 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
425 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
426 },
427 },
428 {
429 .pixel_clock = 74176000,
430 .conf = {
431 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
432 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
433 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
434 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
435 },
436 },
437 {
438 .pixel_clock = 74250000,
439 .conf = {
440 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
441 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
442 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
443 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
444 },
445 },
446 {
447 .pixel_clock = 83500000,
448 .conf = {
449 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
450 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
451 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
452 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
453 },
454 },
455 {
456 .pixel_clock = 88750000,
457 .conf = {
458 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
459 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
460 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
461 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
462 },
463 },
464 {
465 .pixel_clock = 106500000,
466 .conf = {
467 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
468 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
469 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
470 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
471 },
472 },
473 {
474 .pixel_clock = 108000000,
475 .conf = {
476 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
477 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
478 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
479 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
480 },
481 },
482 {
483 .pixel_clock = 115500000,
484 .conf = {
485 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
486 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
487 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
488 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
489 },
490 },
491 {
492 .pixel_clock = 146250000,
493 .conf = {
494 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
495 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
496 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
497 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
498 },
499 },
500 {
501 .pixel_clock = 148500000,
502 .conf = {
503 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
504 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
505 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
506 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
507 },
508 },
509};
510
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900511static const struct hdmiphy_config hdmiphy_5433_configs[] = {
512 {
513 .pixel_clock = 27000000,
514 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100515 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
516 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
517 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900518 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
519 },
520 },
521 {
522 .pixel_clock = 27027000,
523 .conf = {
524 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100525 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
526 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
527 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900528 },
529 },
530 {
531 .pixel_clock = 40000000,
532 .conf = {
533 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
534 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
535 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
536 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
537 },
538 },
539 {
540 .pixel_clock = 50000000,
541 .conf = {
542 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
543 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
544 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
545 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
546 },
547 },
548 {
549 .pixel_clock = 65000000,
550 .conf = {
551 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
552 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
553 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
554 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
555 },
556 },
557 {
558 .pixel_clock = 74176000,
559 .conf = {
560 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
561 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
562 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
563 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
564 },
565 },
566 {
567 .pixel_clock = 74250000,
568 .conf = {
569 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
570 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
571 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
572 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
573 },
574 },
575 {
576 .pixel_clock = 108000000,
577 .conf = {
578 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
579 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
580 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
581 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
582 },
583 },
584 {
585 .pixel_clock = 148500000,
586 .conf = {
587 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
588 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
589 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
590 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
591 },
592 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100593 {
594 .pixel_clock = 297000000,
595 .conf = {
596 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
597 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
598 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
599 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
600 },
601 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900602};
603
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100604static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900605 "hdmi", "sclk_hdmi"
606};
607
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100608static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900609 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
610};
611
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900612static const char * const hdmi_clk_gates5433[] = {
613 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
614};
615
616static const char * const hdmi_clk_muxes5433[] = {
617 "oscclk", "tmds_clko", "tmds_clko_user",
618 "oscclk", "pixel_clko", "pixel_clko_user"
619};
620
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900621static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
622 .type = HDMI_TYPE13,
623 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900624 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
625 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530626};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900627
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100628static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900629 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100630 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900631 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
632 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900633};
634
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900635static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
636 .type = HDMI_TYPE14,
637 .is_apb_phy = 1,
638 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900639 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
640 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200641};
642
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900643static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
644 .type = HDMI_TYPE14,
645 .is_apb_phy = 1,
646 .has_sysreg = 1,
647 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
648 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
649 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
650};
651
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200652static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
653{
654 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
655 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
656 return reg_id;
657}
658
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900659static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
660{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200661 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900662}
663
664static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
665 u32 reg_id, u8 value)
666{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200667 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900668}
669
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200670static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
671 int bytes, u32 val)
672{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200673 reg_id = hdmi_map_reg(hdata, reg_id);
674
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200675 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200676 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200677 val >>= 8;
678 reg_id += 4;
679 }
680}
681
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100682static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
683 u8 *buf, int size)
684{
685 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
686 writel(*buf++, hdata->regs + reg_id);
687}
688
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900689static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
690 u32 reg_id, u32 value, u32 mask)
691{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200692 u32 old;
693
694 reg_id = hdmi_map_reg(hdata, reg_id);
695 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900696 value = (value & mask) | (old & ~mask);
697 writel(value, hdata->regs + reg_id);
698}
699
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900700static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
701 u32 reg_offset, const u8 *buf, u32 len)
702{
703 if ((reg_offset + len) > 32)
704 return -EINVAL;
705
706 if (hdata->hdmiphy_port) {
707 int ret;
708
709 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
710 if (ret == len)
711 return 0;
712 return ret;
713 } else {
714 int i;
715 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200716 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900717 ((reg_offset + i)<<2));
718 return 0;
719 }
720}
721
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900722static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
723{
724 int i, ret;
725
726 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
727 ret = clk_prepare_enable(hdata->clk_gates[i]);
728 if (!ret)
729 continue;
730
731 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
732 hdata->drv_data->clk_gates.data[i], ret);
733 while (i--)
734 clk_disable_unprepare(hdata->clk_gates[i]);
735 return ret;
736 }
737
738 return 0;
739}
740
741static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
742{
743 int i = hdata->drv_data->clk_gates.count;
744
745 while (i--)
746 clk_disable_unprepare(hdata->clk_gates[i]);
747}
748
749static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
750{
751 struct device *dev = hdata->dev;
752 int ret = 0;
753 int i;
754
755 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
756 struct clk **c = &hdata->clk_muxes[i];
757
758 ret = clk_set_parent(c[2], c[to_phy]);
759 if (!ret)
760 continue;
761
762 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
763 hdata->drv_data->clk_muxes.data[i + 2],
764 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
765 }
766
767 return ret;
768}
769
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100770static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530771{
Andrzej Hajda991ea042017-09-29 12:05:37 +0200772 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100773 union hdmi_infoframe frm;
774 u8 buf[25];
775 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530776
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530777 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530778 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
779 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100780 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
781 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530782 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
783 return;
784 }
785
Andrzej Hajda991ea042017-09-29 12:05:37 +0200786 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi, m, false);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100787 if (!ret)
788 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
789 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530790 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100791 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
792 } else {
793 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
794 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530795
Andrzej Hajda991ea042017-09-29 12:05:37 +0200796 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi, m);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100797 if (!ret)
798 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
799 sizeof(buf));
800 if (ret > 0) {
801 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100802 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
803 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100804 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530805
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100806 ret = hdmi_audio_infoframe_init(&frm.audio);
807 if (!ret) {
808 frm.audio.channels = 2;
809 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
810 }
811 if (ret > 0) {
812 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
813 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530814 }
815}
816
Sean Pauld9716ee2014-01-30 16:19:29 -0500817static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
818 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500819{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200820 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500821
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200822 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200823 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530824
Hans Verkuil278c8112016-12-13 11:07:17 -0200825 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200826 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500827}
828
Sean Pauld9716ee2014-01-30 16:19:29 -0500829static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900830{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200831 drm_connector_unregister(connector);
832 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900833}
834
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100835static const struct drm_connector_funcs hdmi_connector_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500836 .fill_modes = drm_helper_probe_single_connector_modes,
837 .detect = hdmi_detect,
838 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300839 .reset = drm_atomic_helper_connector_reset,
840 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
841 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500842};
843
844static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900845{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200846 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500847 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200848 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900849
Inki Dae8fa04aa2014-03-13 16:38:31 +0900850 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500851 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900852
Inki Dae8fa04aa2014-03-13 16:38:31 +0900853 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500854 if (!edid)
855 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900856
Sean Pauld9716ee2014-01-30 16:19:29 -0500857 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500858 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
859 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500860 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500861
Sean Pauld9716ee2014-01-30 16:19:29 -0500862 drm_mode_connector_update_edid_property(connector, edid);
Hans Verkuil278c8112016-12-13 11:07:17 -0200863 cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
Sean Pauld9716ee2014-01-30 16:19:29 -0500864
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200865 ret = drm_add_edid_modes(connector, edid);
866
867 kfree(edid);
868
869 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870}
871
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900872static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900873{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100874 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900875 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900876
Andrzej Hajda65e98032015-11-02 14:16:41 +0100877 for (i = 0; i < confs->count; i++)
878 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500879 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500880
881 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
882 return -EINVAL;
883}
884
Sean Pauld9716ee2014-01-30 16:19:29 -0500885static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500886 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900887{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200888 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900889 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900890
Rahul Sharma16844fb2013-06-10 14:50:00 +0530891 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
892 mode->hdisplay, mode->vdisplay, mode->vrefresh,
893 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
894 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900895
Rahul Sharma16844fb2013-06-10 14:50:00 +0530896 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900897 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500898 return MODE_BAD;
899
900 return MODE_OK;
901}
902
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100903static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500904 .get_modes = hdmi_get_modes,
905 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500906};
907
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300908static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500909{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300910 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500911 struct drm_connector *connector = &hdata->connector;
912 int ret;
913
Sean Pauld9716ee2014-01-30 16:19:29 -0500914 connector->interlace_allowed = true;
915 connector->polled = DRM_CONNECTOR_POLL_HPD;
916
917 ret = drm_connector_init(hdata->drm_dev, connector,
918 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
919 if (ret) {
920 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900921 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500922 }
923
924 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300925 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500926
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100927 if (hdata->bridge) {
928 encoder->bridge = hdata->bridge;
929 hdata->bridge->encoder = encoder;
930 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
931 if (ret)
932 DRM_ERROR("Failed to attach bridge\n");
933 }
934
935 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500936}
937
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300938static bool hdmi_mode_fixup(struct drm_encoder *encoder,
939 const struct drm_display_mode *mode,
940 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500941{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300942 struct drm_device *dev = encoder->dev;
943 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500944 struct drm_display_mode *m;
Maciej Purski04fc52f2017-09-05 14:23:02 +0200945 struct drm_connector_list_iter conn_iter;
Sean Paulf041b252014-01-30 16:19:15 -0500946 int mode_ok;
947
Sean Paulf041b252014-01-30 16:19:15 -0500948 drm_mode_set_crtcinfo(adjusted_mode, 0);
949
Maciej Purski04fc52f2017-09-05 14:23:02 +0200950 drm_connector_list_iter_begin(dev, &conn_iter);
951 drm_for_each_connector_iter(connector, &conn_iter) {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300952 if (connector->encoder == encoder)
953 break;
954 }
Maciej Purski04fc52f2017-09-05 14:23:02 +0200955 if (connector)
956 drm_connector_get(connector);
957 drm_connector_list_iter_end(&conn_iter);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300958
Maciej Purski04fc52f2017-09-05 14:23:02 +0200959 if (!connector)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300960 return true;
961
Sean Pauld9716ee2014-01-30 16:19:29 -0500962 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500963
Sean Pauld9716ee2014-01-30 16:19:29 -0500964 if (mode_ok == MODE_OK)
Maciej Purski04fc52f2017-09-05 14:23:02 +0200965 goto cleanup;
Sean Paulf041b252014-01-30 16:19:15 -0500966
967 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900968 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500969 */
970 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500971 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500972
Sean Pauld9716ee2014-01-30 16:19:29 -0500973 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500974 DRM_INFO("desired mode doesn't exist so\n");
975 DRM_INFO("use the most suitable mode among modes.\n");
976
977 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
978 m->hdisplay, m->vdisplay, m->vrefresh);
979
Sean Paul75626852014-01-30 16:19:16 -0500980 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500981 break;
982 }
983 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300984
Maciej Purski04fc52f2017-09-05 14:23:02 +0200985cleanup:
986 drm_connector_put(connector);
987
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300988 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500989}
990
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200991static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900992{
993 u32 n, cts;
994
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200995 cts = (freq % 9) ? 27000 : 30000;
996 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900997
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200998 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
999 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
1000 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001001 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001002}
1003
1004static void hdmi_audio_init(struct hdmi_context *hdata)
1005{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301006 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001007 u32 data_num, bit_ch, sample_frq;
1008 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001009
1010 sample_rate = 44100;
1011 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001012
1013 switch (bits_per_sample) {
1014 case 20:
1015 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001016 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001017 break;
1018 case 24:
1019 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001020 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001021 break;
1022 default:
1023 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001024 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001025 break;
1026 }
1027
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001028 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001029
1030 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1031 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1032 | HDMI_I2S_MUX_ENABLE);
1033
1034 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1035 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1036
1037 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1038
1039 sample_frq = (sample_rate == 44100) ? 0 :
1040 (sample_rate == 48000) ? 2 :
1041 (sample_rate == 32000) ? 3 :
1042 (sample_rate == 96000) ? 0xa : 0x0;
1043
1044 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1045 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1046
1047 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1048 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1049
1050 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1051 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1052 | HDMI_I2S_SEL_LRCK(6));
1053 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1054 | HDMI_I2S_SEL_SDATA2(4));
1055 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1056 | HDMI_I2S_SEL_SDATA2(2));
1057 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1058
1059 /* I2S_CON_1 & 2 */
1060 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1061 | HDMI_I2S_L_CH_LOW_POL);
1062 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1063 | HDMI_I2S_SET_BIT_CH(bit_ch)
1064 | HDMI_I2S_SET_SDATA_BIT(data_num)
1065 | HDMI_I2S_BASIC_FORMAT);
1066
1067 /* Configure register related to CUV information */
1068 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1069 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1070 | HDMI_I2S_COPYRIGHT
1071 | HDMI_I2S_LINEAR_PCM
1072 | HDMI_I2S_CONSUMER_FORMAT);
1073 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1074 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1075 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1076 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1077 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1078 HDMI_I2S_ORG_SMP_FREQ_44_1
1079 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1080 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1081
1082 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1083}
1084
1085static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1086{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001087 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001088 return;
1089
1090 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1091 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1092 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1093}
1094
Rahul Sharmabfa48422014-04-03 20:41:04 +05301095static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001096{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001097 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Rahul Sharmabfa48422014-04-03 20:41:04 +05301098 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001099
Andrzej Hajda991ea042017-09-29 12:05:37 +02001100 if (m->flags & DRM_MODE_FLAG_INTERLACE)
Rahul Sharmabfa48422014-04-03 20:41:04 +05301101 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001102
Rahul Sharmabfa48422014-04-03 20:41:04 +05301103 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1104 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001105}
1106
1107static void hdmi_conf_init(struct hdmi_context *hdata)
1108{
Sean Paul77006a72013-01-16 10:17:20 -05001109 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001110 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1111 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001112
1113 /* choose HDMI mode */
1114 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1115 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001116 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301117 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001118 /* disable bluescreen */
1119 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001120
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001121 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001122 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1123 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1124 hdmi_reg_writeb(hdata, HDMI_CON_2,
1125 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1126 }
1127
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001128 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001129 /* choose bluescreen (fecal) color */
1130 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1131 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1132 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1133
1134 /* enable AVI packet every vsync, fixes purple line problem */
1135 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1136 /* force RGB, look to CEA-861-D, table 7 for more detail */
1137 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1138 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1139
1140 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1141 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1142 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1143 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001144 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301145
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001146 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001147 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1148 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001149}
1150
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001151static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1152{
1153 int tries;
1154
1155 for (tries = 0; tries < 10; ++tries) {
1156 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1157
1158 if (val & HDMI_PHY_STATUS_READY) {
1159 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1160 return;
1161 }
1162 usleep_range(10, 20);
1163 }
1164
1165 DRM_ERROR("PLL could not reach steady state\n");
1166}
1167
Rahul Sharma16844fb2013-06-10 14:50:00 +05301168static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001169{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001170 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001171 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001172
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001173 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1174 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1175 (m->htotal << 12) | m->vtotal);
1176
1177 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1178 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1179
1180 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1181 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1182
1183 val = (m->hsync_start - m->hdisplay - 2);
1184 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001185 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001186 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1187
1188 /*
1189 * Quirk requirement for exynos HDMI IP design,
1190 * 2 pixels less than the actual calculation for hsync_start
1191 * and end.
1192 */
1193
1194 /* Following values & calculations differ for different type of modes */
1195 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001196 val = ((m->vsync_end - m->vdisplay) / 2);
1197 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1198 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1199
1200 val = m->vtotal / 2;
1201 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1202 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1203
1204 val = (m->vtotal +
1205 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1206 val |= m->vtotal << 11;
1207 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1208
1209 val = ((m->vtotal / 2) + 7);
1210 val |= ((m->vtotal / 2) + 2) << 12;
1211 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1212
1213 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1214 val |= ((m->htotal / 2) +
1215 (m->hsync_start - m->hdisplay)) << 12;
1216 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1217
1218 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1219 (m->vtotal - m->vdisplay) / 2);
1220 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1221
1222 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1223 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001224 val = m->vtotal;
1225 val |= (m->vtotal - m->vdisplay) << 11;
1226 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1227
1228 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1229
1230 val = (m->vsync_end - m->vdisplay);
1231 val |= ((m->vsync_start - m->vdisplay) << 12);
1232 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1233
1234 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1235 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1236 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1237 m->vtotal - m->vdisplay);
1238 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001239 }
1240
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001241 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1242 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1243 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1244 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001245}
1246
Rahul Sharma16844fb2013-06-10 14:50:00 +05301247static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001248{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001249 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Andrzej Hajda36fd8872017-09-29 12:05:40 +02001250 struct drm_display_mode *am =
1251 &hdata->encoder.crtc->state->adjusted_mode;
1252 int hquirk = 0;
1253
1254 /*
1255 * In case video mode coming from CRTC differs from requested one HDMI
1256 * sometimes is able to almost properly perform conversion - only
1257 * first line is distorted.
1258 */
1259 if ((m->vdisplay != am->vdisplay) &&
1260 (m->hdisplay == 1280 || m->hdisplay == 1024))
1261 hquirk = 258;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001262
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001263 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1264 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1265 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1266 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001267 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001268 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1269 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1270 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1271 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1272
1273 /*
1274 * Quirk requirement for exynos 5 HDMI IP design,
1275 * 2 pixels less than the actual calculation for hsync_start
1276 * and end.
1277 */
1278
1279 /* Following values & calculations differ for different type of modes */
1280 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001281 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1282 (m->vsync_end - m->vdisplay) / 2);
1283 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1284 (m->vsync_start - m->vdisplay) / 2);
1285 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1286 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1287 (m->vtotal - m->vdisplay) / 2);
1288 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1289 m->vtotal - m->vdisplay / 2);
1290 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1291 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1292 (m->vtotal / 2) + 7);
1293 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1294 (m->vtotal / 2) + 2);
1295 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1296 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1297 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1298 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1299 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1300 (m->vtotal - m->vdisplay) / 2);
1301 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1302 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1303 m->vtotal - m->vdisplay / 2);
1304 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1305 (m->vtotal / 2) + 1);
1306 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1307 (m->vtotal / 2) + 1);
1308 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1309 (m->vtotal / 2) + 1);
1310 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1311 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1312 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001313 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1314 m->vsync_end - m->vdisplay);
1315 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1316 m->vsync_start - m->vdisplay);
1317 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1318 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1319 m->vtotal - m->vdisplay);
1320 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1322 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1323 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1327 m->vtotal - m->vdisplay);
1328 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001329 }
1330
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001331 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1332 m->hsync_start - m->hdisplay - 2);
1333 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1334 m->hsync_end - m->hdisplay - 2);
1335 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1336 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1337 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1338 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1339 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1340 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1341 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1342 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1343 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1344 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1345 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1346 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1347 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1348 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1349 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1350 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1351 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1352 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001353
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001354 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
Andrzej Hajda36fd8872017-09-29 12:05:40 +02001355 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2,
1356 m->htotal - m->hdisplay - hquirk);
1357 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay + hquirk);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001358 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001359 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1360 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001361}
1362
Rahul Sharma16844fb2013-06-10 14:50:00 +05301363static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001364{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001365 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301366 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001367 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301368 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001369
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001370 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001371}
1372
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001373static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1374{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001375 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1376 usleep_range(10000, 12000);
1377 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1378 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001379 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001380 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001381 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001382 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001383}
1384
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001385static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1386{
1387 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1388
1389 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1390 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1391}
1392
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001393static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1394{
Andrzej Hajda991ea042017-09-29 12:05:37 +02001395 struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001396 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001397 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001398
Andrzej Hajda991ea042017-09-29 12:05:37 +02001399 ret = hdmi_find_phy_conf(hdata, m->clock * 1000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001400 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001401 DRM_ERROR("failed to find hdmiphy conf\n");
1402 return;
1403 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001404 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1405
1406 hdmi_clk_set_parents(hdata, false);
1407
1408 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001409
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001410 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001411 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001412 if (ret) {
1413 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001414 return;
1415 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001416 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001417 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001418 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001419 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001420}
1421
1422static void hdmi_conf_apply(struct hdmi_context *hdata)
1423{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301424 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001425 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001426 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301427 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001428 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001429}
1430
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001431static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1432{
1433 if (!hdata->sysreg)
1434 return;
1435
1436 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1437 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1438}
1439
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001440static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001441{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001442 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001443 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001444
Sean Paulaf65c802014-01-30 16:19:27 -05001445 pm_runtime_get_sync(hdata->dev);
1446
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001447 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001448 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1449
Rahul Sharma049d34e2014-05-20 10:36:05 +05301450 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1451 PMU_HDMI_PHY_ENABLE_BIT, 1);
1452
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001453 hdmi_set_refclk(hdata, true);
1454
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001455 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1456
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001457 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001458
1459 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001460}
1461
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001462static void hdmiphy_disable(struct hdmi_context *hdata)
1463{
1464 if (!hdata->powered)
1465 return;
1466
1467 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1468
1469 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1470
1471 hdmi_set_refclk(hdata, false);
1472
1473 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1474 PMU_HDMI_PHY_ENABLE_BIT, 0);
1475
1476 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1477
1478 pm_runtime_put_sync(hdata->dev);
1479
1480 hdata->powered = false;
1481}
1482
1483static void hdmi_enable(struct drm_encoder *encoder)
1484{
1485 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1486
1487 hdmiphy_enable(hdata);
1488 hdmi_conf_apply(hdata);
1489}
1490
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001491static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001492{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001493 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001494
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001495 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001496 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001497
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001498 /*
1499 * The SFRs of VP and Mixer are updated by Vertical Sync of
1500 * Timing generator which is a part of HDMI so the sequence
1501 * to disable TV Subsystem should be as following,
1502 * VP -> Mixer -> HDMI
1503 *
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001504 * To achieve such sequence HDMI is disabled together with HDMI PHY, via
1505 * pipe clock callback.
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001506 */
Sean Paul724fd142014-05-09 15:05:10 +09001507 cancel_delayed_work(&hdata->hotplug_work);
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001508 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001509}
1510
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001511static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001512 .mode_fixup = hdmi_mode_fixup,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001513 .enable = hdmi_enable,
1514 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001515};
1516
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001517static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001518 .destroy = drm_encoder_cleanup,
1519};
1520
Sean Paul724fd142014-05-09 15:05:10 +09001521static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001522{
Sean Paul724fd142014-05-09 15:05:10 +09001523 struct hdmi_context *hdata;
1524
1525 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001526
Sean Paul45517892014-01-30 16:19:05 -05001527 if (hdata->drm_dev)
1528 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001529}
1530
1531static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1532{
1533 struct hdmi_context *hdata = arg;
1534
1535 mod_delayed_work(system_wq, &hdata->hotplug_work,
1536 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001537
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001538 return IRQ_HANDLED;
1539}
1540
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001541static int hdmi_clks_get(struct hdmi_context *hdata,
1542 const struct string_array_spec *names,
1543 struct clk **clks)
1544{
1545 struct device *dev = hdata->dev;
1546 int i;
1547
1548 for (i = 0; i < names->count; ++i) {
1549 struct clk *clk = devm_clk_get(dev, names->data[i]);
1550
1551 if (IS_ERR(clk)) {
1552 int ret = PTR_ERR(clk);
1553
1554 dev_err(dev, "Cannot get clock %s, %d\n",
1555 names->data[i], ret);
1556
1557 return ret;
1558 }
1559
1560 clks[i] = clk;
1561 }
1562
1563 return 0;
1564}
1565
1566static int hdmi_clk_init(struct hdmi_context *hdata)
1567{
1568 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1569 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1570 struct device *dev = hdata->dev;
1571 struct clk **clks;
1572 int ret;
1573
1574 if (!count)
1575 return 0;
1576
1577 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1578 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001579 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001580
1581 hdata->clk_gates = clks;
1582 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1583
1584 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1585 if (ret)
1586 return ret;
1587
1588 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1589}
1590
1591
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001592static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1593{
1594 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1595 phy_clk);
1596
1597 if (enable)
1598 hdmiphy_enable(hdata);
1599 else
1600 hdmiphy_disable(hdata);
1601}
1602
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001603static int hdmi_bridge_init(struct hdmi_context *hdata)
1604{
1605 struct device *dev = hdata->dev;
1606 struct device_node *ep, *np;
1607
1608 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1609 if (!ep)
1610 return 0;
1611
1612 np = of_graph_get_remote_port_parent(ep);
1613 of_node_put(ep);
1614 if (!np) {
1615 DRM_ERROR("failed to get remote port parent");
1616 return -EINVAL;
1617 }
1618
1619 hdata->bridge = of_drm_find_bridge(np);
1620 of_node_put(np);
1621
1622 if (!hdata->bridge)
1623 return -EPROBE_DEFER;
1624
1625 return 0;
1626}
1627
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001628static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001629{
1630 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001631 int i, ret;
1632
1633 DRM_DEBUG_KMS("HDMI resource init\n");
1634
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001635 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1636 if (IS_ERR(hdata->hpd_gpio)) {
1637 DRM_ERROR("cannot get hpd gpio property\n");
1638 return PTR_ERR(hdata->hpd_gpio);
1639 }
1640
1641 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1642 if (hdata->irq < 0) {
1643 DRM_ERROR("failed to get GPIO irq\n");
1644 return hdata->irq;
1645 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001646
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001647 ret = hdmi_clk_init(hdata);
1648 if (ret)
1649 return ret;
1650
1651 ret = hdmi_clk_set_parents(hdata, false);
1652 if (ret)
1653 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001654
Milo Kimc0d656d2016-08-31 15:14:27 +09001655 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001656 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001657
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001658 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001659 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001660 if (ret != -EPROBE_DEFER)
1661 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001662 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001663 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001664
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001665 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001666
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001667 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1668 if (IS_ERR(hdata->reg_hdmi_en))
1669 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001670
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001671 ret = regulator_enable(hdata->reg_hdmi_en);
1672 if (ret) {
1673 DRM_ERROR("failed to enable hdmi-en regulator\n");
1674 return ret;
1675 }
1676 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001677
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001678 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001679}
1680
Arvind Yadave3cc51e2017-06-19 14:54:03 +05301681static const struct of_device_id hdmi_match_types[] = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301682 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001683 .compatible = "samsung,exynos4210-hdmi",
1684 .data = &exynos4210_hdmi_driver_data,
1685 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301686 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001687 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301688 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301689 .compatible = "samsung,exynos5420-hdmi",
1690 .data = &exynos5420_hdmi_driver_data,
1691 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001692 .compatible = "samsung,exynos5433-hdmi",
1693 .data = &exynos5433_hdmi_driver_data,
1694 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301695 /* end node */
1696 }
1697};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001698MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301699
Inki Daef37cd5e2014-05-09 14:25:20 +09001700static int hdmi_bind(struct device *dev, struct device *master, void *data)
1701{
1702 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001703 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001704 struct drm_encoder *encoder = &hdata->encoder;
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001705 struct exynos_drm_crtc *crtc;
1706 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001707
Inki Daef37cd5e2014-05-09 14:25:20 +09001708 hdata->drm_dev = drm_dev;
1709
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001710 hdata->phy_clk.enable = hdmiphy_clk_enable;
1711
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001712 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001713 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001714
1715 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1716
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001717 ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_HDMI);
1718 if (ret < 0)
1719 return ret;
1720
1721 crtc = exynos_drm_crtc_get_by_type(drm_dev, EXYNOS_DISPLAY_TYPE_HDMI);
1722 crtc->pipe_clk = &hdata->phy_clk;
1723
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001724 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001725 if (ret) {
1726 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001727 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001728 return ret;
1729 }
1730
1731 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001732}
1733
1734static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1735{
Inki Daef37cd5e2014-05-09 14:25:20 +09001736}
1737
1738static const struct component_ops hdmi_component_ops = {
1739 .bind = hdmi_bind,
1740 .unbind = hdmi_unbind,
1741};
1742
Milo Kim1caa3602016-08-31 15:14:25 +09001743static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001744{
1745 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1746 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001747 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001748
1749 np = of_find_compatible_node(NULL, NULL, compatible_str);
1750 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001751 np = of_get_next_parent(np);
1752 else
1753 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001754
Milo Kim1caa3602016-08-31 15:14:25 +09001755 if (!np) {
1756 DRM_ERROR("Failed to find ddc node in device tree\n");
1757 return -ENODEV;
1758 }
1759
1760 adpt = of_find_i2c_adapter_by_node(np);
1761 of_node_put(np);
1762
1763 if (!adpt) {
1764 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1765 return -EPROBE_DEFER;
1766 }
1767
1768 hdata->ddc_adpt = adpt;
1769
1770 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001771}
1772
Milo Kimb5413022016-08-31 15:14:26 +09001773static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001774{
1775 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001776 struct device_node *np;
1777 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001778
Milo Kimb5413022016-08-31 15:14:26 +09001779 np = of_find_compatible_node(NULL, NULL, compatible_str);
1780 if (!np) {
1781 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1782 if (!np) {
1783 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1784 return -ENODEV;
1785 }
1786 }
1787
1788 if (hdata->drv_data->is_apb_phy) {
1789 hdata->regs_hdmiphy = of_iomap(np, 0);
1790 if (!hdata->regs_hdmiphy) {
1791 DRM_ERROR("failed to ioremap hdmi phy\n");
1792 ret = -ENOMEM;
1793 goto out;
1794 }
1795 } else {
1796 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1797 if (!hdata->hdmiphy_port) {
1798 DRM_INFO("Failed to get hdmi phy i2c client\n");
1799 ret = -EPROBE_DEFER;
1800 goto out;
1801 }
1802 }
1803
1804out:
1805 of_node_put(np);
1806 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001807}
1808
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001809static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001810{
1811 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001812 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001813 struct resource *res;
1814 int ret;
1815
Andrzej Hajda930865f2014-11-17 09:54:20 +01001816 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1817 if (!hdata)
1818 return -ENOMEM;
1819
Marek Szyprowski57a64122016-04-01 15:17:44 +02001820 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001821
Andrzej Hajda930865f2014-11-17 09:54:20 +01001822 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001823
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001824 hdata->dev = dev;
1825
1826 ret = hdmi_resources_init(hdata);
1827 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001828 if (ret != -EPROBE_DEFER)
1829 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001830 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001831 }
1832
1833 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001834 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001835 if (IS_ERR(hdata->regs)) {
1836 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001837 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001838 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001839
Milo Kim1caa3602016-08-31 15:14:25 +09001840 ret = hdmi_get_ddc_adapter(hdata);
1841 if (ret)
1842 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001843
Milo Kimb5413022016-08-31 15:14:26 +09001844 ret = hdmi_get_phy_io(hdata);
1845 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001846 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001847
Sean Paul724fd142014-05-09 15:05:10 +09001848 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1849
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001850 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001851 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001852 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001853 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001854 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001855 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001856 goto err_hdmiphy;
1857 }
1858
Rahul Sharma049d34e2014-05-20 10:36:05 +05301859 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1860 "samsung,syscon-phandle");
1861 if (IS_ERR(hdata->pmureg)) {
1862 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001863 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301864 goto err_hdmiphy;
1865 }
1866
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001867 if (hdata->drv_data->has_sysreg) {
1868 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1869 "samsung,sysreg-phandle");
1870 if (IS_ERR(hdata->sysreg)) {
1871 DRM_ERROR("sysreg regmap lookup failed.\n");
1872 ret = -EPROBE_DEFER;
1873 goto err_hdmiphy;
1874 }
1875 }
1876
Hans Verkuil278c8112016-12-13 11:07:17 -02001877 hdata->notifier = cec_notifier_get(&pdev->dev);
1878 if (hdata->notifier == NULL) {
1879 ret = -ENOMEM;
1880 goto err_hdmiphy;
1881 }
1882
Sean Paulaf65c802014-01-30 16:19:27 -05001883 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001884
Inki Daedf5225b2014-05-29 18:28:02 +09001885 ret = component_add(&pdev->dev, &hdmi_component_ops);
1886 if (ret)
Hans Verkuil278c8112016-12-13 11:07:17 -02001887 goto err_notifier_put;
Inki Daedf5225b2014-05-29 18:28:02 +09001888
1889 return ret;
1890
Hans Verkuil278c8112016-12-13 11:07:17 -02001891err_notifier_put:
1892 cec_notifier_put(hdata->notifier);
Inki Daedf5225b2014-05-29 18:28:02 +09001893 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001894
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001895err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001896 if (hdata->hdmiphy_port)
1897 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301898 if (hdata->regs_hdmiphy)
1899 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001900err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001901 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001902
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001903 return ret;
1904}
1905
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001906static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001907{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001908 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001909
Sean Paul724fd142014-05-09 15:05:10 +09001910 cancel_delayed_work_sync(&hdata->hotplug_work);
Hans Verkuil278c8112016-12-13 11:07:17 -02001911 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09001912
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001913 component_del(&pdev->dev, &hdmi_component_ops);
1914
Hans Verkuil278c8112016-12-13 11:07:17 -02001915 cec_notifier_put(hdata->notifier);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001916 pm_runtime_disable(&pdev->dev);
1917
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001918 if (!IS_ERR(hdata->reg_hdmi_en))
1919 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001920
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001921 if (hdata->hdmiphy_port)
1922 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001923
Arvind Yadavd7420002016-10-19 15:34:16 +05301924 if (hdata->regs_hdmiphy)
1925 iounmap(hdata->regs_hdmiphy);
1926
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001927 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001928
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001929 return 0;
1930}
1931
Arnd Bergmann7e175102017-07-21 22:47:18 +02001932static int __maybe_unused exynos_hdmi_suspend(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001933{
1934 struct hdmi_context *hdata = dev_get_drvdata(dev);
1935
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001936 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001937
1938 return 0;
1939}
1940
Arnd Bergmann7e175102017-07-21 22:47:18 +02001941static int __maybe_unused exynos_hdmi_resume(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001942{
1943 struct hdmi_context *hdata = dev_get_drvdata(dev);
1944 int ret;
1945
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001946 ret = hdmi_clk_enable_gates(hdata);
1947 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001948 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001949
1950 return 0;
1951}
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001952
1953static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1954 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1955};
1956
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001957struct platform_driver hdmi_driver = {
1958 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001959 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001960 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301961 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001962 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001963 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301964 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001965 },
1966};