blob: 214fa5e51963898ee5288f98d536681cdbebdcd6 [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;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530122 struct drm_display_mode current_mode;
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;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900140};
141
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300142static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100143{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900144 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100145}
146
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200147static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
148{
149 return container_of(c, struct hdmi_context, connector);
150}
151
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900152static const struct hdmiphy_config hdmiphy_v13_configs[] = {
153 {
154 .pixel_clock = 27000000,
155 .conf = {
156 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
157 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
158 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200159 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900160 },
161 },
162 {
163 .pixel_clock = 27027000,
164 .conf = {
165 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
166 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
167 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200168 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900169 },
170 },
171 {
172 .pixel_clock = 74176000,
173 .conf = {
174 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
175 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
176 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200177 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900178 },
179 },
180 {
181 .pixel_clock = 74250000,
182 .conf = {
183 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
184 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
185 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200186 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900187 },
188 },
189 {
190 .pixel_clock = 148500000,
191 .conf = {
192 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
193 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
194 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200195 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900196 },
197 },
198};
199
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500200static const struct hdmiphy_config hdmiphy_v14_configs[] = {
201 {
202 .pixel_clock = 25200000,
203 .conf = {
204 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
205 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
206 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
207 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
208 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900209 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500210 {
211 .pixel_clock = 27000000,
212 .conf = {
213 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
214 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
215 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
216 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
217 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900218 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500219 {
220 .pixel_clock = 27027000,
221 .conf = {
222 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
223 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
224 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200225 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500226 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900227 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500228 {
229 .pixel_clock = 36000000,
230 .conf = {
231 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
232 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
233 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
234 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
235 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900236 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500237 {
238 .pixel_clock = 40000000,
239 .conf = {
240 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
241 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
242 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
243 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
244 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900245 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500246 {
247 .pixel_clock = 65000000,
248 .conf = {
249 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
250 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
251 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
252 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
253 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900254 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500255 {
Shirish Se1d883c2014-03-13 14:28:27 +0900256 .pixel_clock = 71000000,
257 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530258 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
259 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
260 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900261 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
262 },
263 },
264 {
265 .pixel_clock = 73250000,
266 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530267 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
268 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
269 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900270 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
271 },
272 },
273 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500274 .pixel_clock = 74176000,
275 .conf = {
276 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
277 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
278 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
279 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
280 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900281 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500282 {
283 .pixel_clock = 74250000,
284 .conf = {
285 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
286 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
287 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200288 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500289 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900290 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500291 {
292 .pixel_clock = 83500000,
293 .conf = {
294 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
295 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
296 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
297 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
298 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900299 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500300 {
301 .pixel_clock = 106500000,
302 .conf = {
303 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
304 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
305 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
306 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
307 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900308 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500309 {
310 .pixel_clock = 108000000,
311 .conf = {
312 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
313 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
314 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
315 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
316 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900317 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500318 {
Shirish Se1d883c2014-03-13 14:28:27 +0900319 .pixel_clock = 115500000,
320 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530321 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
322 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
323 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900324 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
325 },
326 },
327 {
328 .pixel_clock = 119000000,
329 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530330 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
331 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
332 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900333 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
334 },
335 },
336 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500337 .pixel_clock = 146250000,
338 .conf = {
339 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
340 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
341 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
342 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
343 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900344 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500345 {
346 .pixel_clock = 148500000,
347 .conf = {
348 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
349 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
350 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200351 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500352 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900353 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900354};
355
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530356static const struct hdmiphy_config hdmiphy_5420_configs[] = {
357 {
358 .pixel_clock = 25200000,
359 .conf = {
360 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
361 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
362 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
363 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
364 },
365 },
366 {
367 .pixel_clock = 27000000,
368 .conf = {
369 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
370 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
371 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
372 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
373 },
374 },
375 {
376 .pixel_clock = 27027000,
377 .conf = {
378 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
379 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
380 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
381 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
382 },
383 },
384 {
385 .pixel_clock = 36000000,
386 .conf = {
387 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
388 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
389 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
390 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
391 },
392 },
393 {
394 .pixel_clock = 40000000,
395 .conf = {
396 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
397 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
398 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
399 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
400 },
401 },
402 {
403 .pixel_clock = 65000000,
404 .conf = {
405 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
406 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
407 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
408 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
409 },
410 },
411 {
412 .pixel_clock = 71000000,
413 .conf = {
414 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
415 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
416 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
417 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
418 },
419 },
420 {
421 .pixel_clock = 73250000,
422 .conf = {
423 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
424 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
425 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
426 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
427 },
428 },
429 {
430 .pixel_clock = 74176000,
431 .conf = {
432 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
433 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
434 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
435 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
436 },
437 },
438 {
439 .pixel_clock = 74250000,
440 .conf = {
441 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
442 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
443 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
444 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
445 },
446 },
447 {
448 .pixel_clock = 83500000,
449 .conf = {
450 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
451 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
452 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
453 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
454 },
455 },
456 {
457 .pixel_clock = 88750000,
458 .conf = {
459 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
460 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
461 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
462 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
463 },
464 },
465 {
466 .pixel_clock = 106500000,
467 .conf = {
468 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
469 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
470 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
471 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
472 },
473 },
474 {
475 .pixel_clock = 108000000,
476 .conf = {
477 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
478 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
479 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
480 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
481 },
482 },
483 {
484 .pixel_clock = 115500000,
485 .conf = {
486 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
487 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
488 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
489 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
490 },
491 },
492 {
493 .pixel_clock = 146250000,
494 .conf = {
495 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
496 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
497 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
498 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
499 },
500 },
501 {
502 .pixel_clock = 148500000,
503 .conf = {
504 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
505 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
506 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
507 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
508 },
509 },
510};
511
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900512static const struct hdmiphy_config hdmiphy_5433_configs[] = {
513 {
514 .pixel_clock = 27000000,
515 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100516 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
517 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
518 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900519 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
520 },
521 },
522 {
523 .pixel_clock = 27027000,
524 .conf = {
525 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100526 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
527 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
528 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900529 },
530 },
531 {
532 .pixel_clock = 40000000,
533 .conf = {
534 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
535 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
536 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
537 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
538 },
539 },
540 {
541 .pixel_clock = 50000000,
542 .conf = {
543 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
544 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
545 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
546 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
547 },
548 },
549 {
550 .pixel_clock = 65000000,
551 .conf = {
552 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
553 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
554 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
555 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
556 },
557 },
558 {
559 .pixel_clock = 74176000,
560 .conf = {
561 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
562 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
563 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
564 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
565 },
566 },
567 {
568 .pixel_clock = 74250000,
569 .conf = {
570 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
571 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
572 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
573 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
574 },
575 },
576 {
577 .pixel_clock = 108000000,
578 .conf = {
579 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
580 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
581 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
582 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
583 },
584 },
585 {
586 .pixel_clock = 148500000,
587 .conf = {
588 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
589 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
590 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
591 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
592 },
593 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100594 {
595 .pixel_clock = 297000000,
596 .conf = {
597 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
598 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
599 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
600 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
601 },
602 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900603};
604
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100605static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900606 "hdmi", "sclk_hdmi"
607};
608
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100609static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900610 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
611};
612
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900613static const char * const hdmi_clk_gates5433[] = {
614 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
615};
616
617static const char * const hdmi_clk_muxes5433[] = {
618 "oscclk", "tmds_clko", "tmds_clko_user",
619 "oscclk", "pixel_clko", "pixel_clko_user"
620};
621
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900622static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
623 .type = HDMI_TYPE13,
624 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900625 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
626 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530627};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900628
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100629static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900630 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100631 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900632 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
633 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900634};
635
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900636static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
637 .type = HDMI_TYPE14,
638 .is_apb_phy = 1,
639 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900640 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
641 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200642};
643
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900644static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
645 .type = HDMI_TYPE14,
646 .is_apb_phy = 1,
647 .has_sysreg = 1,
648 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
649 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
650 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
651};
652
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200653static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
654{
655 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
656 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
657 return reg_id;
658}
659
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900660static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
661{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200662 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900663}
664
665static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
666 u32 reg_id, u8 value)
667{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200668 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900669}
670
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200671static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
672 int bytes, u32 val)
673{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200674 reg_id = hdmi_map_reg(hdata, reg_id);
675
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200676 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200677 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200678 val >>= 8;
679 reg_id += 4;
680 }
681}
682
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100683static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
684 u8 *buf, int size)
685{
686 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
687 writel(*buf++, hdata->regs + reg_id);
688}
689
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900690static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
691 u32 reg_id, u32 value, u32 mask)
692{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200693 u32 old;
694
695 reg_id = hdmi_map_reg(hdata, reg_id);
696 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900697 value = (value & mask) | (old & ~mask);
698 writel(value, hdata->regs + reg_id);
699}
700
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900701static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
702 u32 reg_offset, const u8 *buf, u32 len)
703{
704 if ((reg_offset + len) > 32)
705 return -EINVAL;
706
707 if (hdata->hdmiphy_port) {
708 int ret;
709
710 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
711 if (ret == len)
712 return 0;
713 return ret;
714 } else {
715 int i;
716 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200717 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900718 ((reg_offset + i)<<2));
719 return 0;
720 }
721}
722
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900723static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
724{
725 int i, ret;
726
727 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
728 ret = clk_prepare_enable(hdata->clk_gates[i]);
729 if (!ret)
730 continue;
731
732 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
733 hdata->drv_data->clk_gates.data[i], ret);
734 while (i--)
735 clk_disable_unprepare(hdata->clk_gates[i]);
736 return ret;
737 }
738
739 return 0;
740}
741
742static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
743{
744 int i = hdata->drv_data->clk_gates.count;
745
746 while (i--)
747 clk_disable_unprepare(hdata->clk_gates[i]);
748}
749
750static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
751{
752 struct device *dev = hdata->dev;
753 int ret = 0;
754 int i;
755
756 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
757 struct clk **c = &hdata->clk_muxes[i];
758
759 ret = clk_set_parent(c[2], c[to_phy]);
760 if (!ret)
761 continue;
762
763 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
764 hdata->drv_data->clk_muxes.data[i + 2],
765 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
766 }
767
768 return ret;
769}
770
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100771static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530772{
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 Hajda5f9e2282016-11-07 16:04:43 +0100786 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
Shashank Sharma0c1f5282017-07-13 21:03:07 +0530787 &hdata->current_mode, false);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100788 if (!ret)
789 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
790 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530791 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100792 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
793 } else {
794 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
795 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530796
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100797 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
798 &hdata->current_mode);
799 if (!ret)
800 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
801 sizeof(buf));
802 if (ret > 0) {
803 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100804 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
805 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100806 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530807
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100808 ret = hdmi_audio_infoframe_init(&frm.audio);
809 if (!ret) {
810 frm.audio.channels = 2;
811 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
812 }
813 if (ret > 0) {
814 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
815 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530816 }
817}
818
Sean Pauld9716ee2014-01-30 16:19:29 -0500819static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
820 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500821{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200822 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500823
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200824 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200825 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530826
Hans Verkuil278c8112016-12-13 11:07:17 -0200827 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200828 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500829}
830
Sean Pauld9716ee2014-01-30 16:19:29 -0500831static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900832{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200833 drm_connector_unregister(connector);
834 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900835}
836
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100837static const struct drm_connector_funcs hdmi_connector_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500838 .fill_modes = drm_helper_probe_single_connector_modes,
839 .detect = hdmi_detect,
840 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300841 .reset = drm_atomic_helper_connector_reset,
842 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
843 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500844};
845
846static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900847{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200848 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500849 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200850 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900851
Inki Dae8fa04aa2014-03-13 16:38:31 +0900852 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500853 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900854
Inki Dae8fa04aa2014-03-13 16:38:31 +0900855 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500856 if (!edid)
857 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900858
Sean Pauld9716ee2014-01-30 16:19:29 -0500859 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500860 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
861 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500862 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500863
Sean Pauld9716ee2014-01-30 16:19:29 -0500864 drm_mode_connector_update_edid_property(connector, edid);
Hans Verkuil278c8112016-12-13 11:07:17 -0200865 cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
Sean Pauld9716ee2014-01-30 16:19:29 -0500866
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200867 ret = drm_add_edid_modes(connector, edid);
868
869 kfree(edid);
870
871 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900872}
873
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900874static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900875{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100876 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900877 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900878
Andrzej Hajda65e98032015-11-02 14:16:41 +0100879 for (i = 0; i < confs->count; i++)
880 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500881 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500882
883 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
884 return -EINVAL;
885}
886
Sean Pauld9716ee2014-01-30 16:19:29 -0500887static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500888 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900889{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200890 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900891 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900892
Rahul Sharma16844fb2013-06-10 14:50:00 +0530893 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
894 mode->hdisplay, mode->vdisplay, mode->vrefresh,
895 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
896 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900897
Rahul Sharma16844fb2013-06-10 14:50:00 +0530898 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900899 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500900 return MODE_BAD;
901
902 return MODE_OK;
903}
904
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100905static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500906 .get_modes = hdmi_get_modes,
907 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500908};
909
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300910static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500911{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300912 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500913 struct drm_connector *connector = &hdata->connector;
914 int ret;
915
Sean Pauld9716ee2014-01-30 16:19:29 -0500916 connector->interlace_allowed = true;
917 connector->polled = DRM_CONNECTOR_POLL_HPD;
918
919 ret = drm_connector_init(hdata->drm_dev, connector,
920 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
921 if (ret) {
922 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900923 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500924 }
925
926 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300927 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500928
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100929 if (hdata->bridge) {
930 encoder->bridge = hdata->bridge;
931 hdata->bridge->encoder = encoder;
932 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
933 if (ret)
934 DRM_ERROR("Failed to attach bridge\n");
935 }
936
937 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500938}
939
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300940static bool hdmi_mode_fixup(struct drm_encoder *encoder,
941 const struct drm_display_mode *mode,
942 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500943{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300944 struct drm_device *dev = encoder->dev;
945 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500946 struct drm_display_mode *m;
947 int mode_ok;
948
Sean Paulf041b252014-01-30 16:19:15 -0500949 drm_mode_set_crtcinfo(adjusted_mode, 0);
950
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300951 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
952 if (connector->encoder == encoder)
953 break;
954 }
955
956 if (connector->encoder != encoder)
957 return true;
958
Sean Pauld9716ee2014-01-30 16:19:29 -0500959 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500960
Sean Pauld9716ee2014-01-30 16:19:29 -0500961 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300962 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500963
964 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900965 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500966 */
967 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500968 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500969
Sean Pauld9716ee2014-01-30 16:19:29 -0500970 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500971 DRM_INFO("desired mode doesn't exist so\n");
972 DRM_INFO("use the most suitable mode among modes.\n");
973
974 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
975 m->hdisplay, m->vdisplay, m->vrefresh);
976
Sean Paul75626852014-01-30 16:19:16 -0500977 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500978 break;
979 }
980 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300981
982 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500983}
984
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200985static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900986{
987 u32 n, cts;
988
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200989 cts = (freq % 9) ? 27000 : 30000;
990 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900991
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200992 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
993 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
994 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200995 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900996}
997
998static void hdmi_audio_init(struct hdmi_context *hdata)
999{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301000 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001001 u32 data_num, bit_ch, sample_frq;
1002 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001003
1004 sample_rate = 44100;
1005 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001006
1007 switch (bits_per_sample) {
1008 case 20:
1009 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001010 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001011 break;
1012 case 24:
1013 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001014 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001015 break;
1016 default:
1017 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001018 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001019 break;
1020 }
1021
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001022 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001023
1024 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1025 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1026 | HDMI_I2S_MUX_ENABLE);
1027
1028 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1029 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1030
1031 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1032
1033 sample_frq = (sample_rate == 44100) ? 0 :
1034 (sample_rate == 48000) ? 2 :
1035 (sample_rate == 32000) ? 3 :
1036 (sample_rate == 96000) ? 0xa : 0x0;
1037
1038 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1039 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1040
1041 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1042 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1043
1044 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1045 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1046 | HDMI_I2S_SEL_LRCK(6));
1047 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1048 | HDMI_I2S_SEL_SDATA2(4));
1049 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1050 | HDMI_I2S_SEL_SDATA2(2));
1051 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1052
1053 /* I2S_CON_1 & 2 */
1054 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1055 | HDMI_I2S_L_CH_LOW_POL);
1056 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1057 | HDMI_I2S_SET_BIT_CH(bit_ch)
1058 | HDMI_I2S_SET_SDATA_BIT(data_num)
1059 | HDMI_I2S_BASIC_FORMAT);
1060
1061 /* Configure register related to CUV information */
1062 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1063 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1064 | HDMI_I2S_COPYRIGHT
1065 | HDMI_I2S_LINEAR_PCM
1066 | HDMI_I2S_CONSUMER_FORMAT);
1067 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1068 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1069 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1070 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1071 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1072 HDMI_I2S_ORG_SMP_FREQ_44_1
1073 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1074 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1075
1076 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1077}
1078
1079static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1080{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001081 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001082 return;
1083
1084 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1085 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1086 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1087}
1088
Rahul Sharmabfa48422014-04-03 20:41:04 +05301089static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001090{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301091 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001092
Rahul Sharmabfa48422014-04-03 20:41:04 +05301093 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1094 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001095
Rahul Sharmabfa48422014-04-03 20:41:04 +05301096 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1097 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001098}
1099
1100static void hdmi_conf_init(struct hdmi_context *hdata)
1101{
Sean Paul77006a72013-01-16 10:17:20 -05001102 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001103 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1104 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001105
1106 /* choose HDMI mode */
1107 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1108 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001109 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301110 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001111 /* disable bluescreen */
1112 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001113
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001114 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001115 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1116 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1117 hdmi_reg_writeb(hdata, HDMI_CON_2,
1118 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1119 }
1120
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001121 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001122 /* choose bluescreen (fecal) color */
1123 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1124 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1125 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1126
1127 /* enable AVI packet every vsync, fixes purple line problem */
1128 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1129 /* force RGB, look to CEA-861-D, table 7 for more detail */
1130 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1131 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1132
1133 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1134 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1135 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1136 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001137 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301138
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001139 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001140 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1141 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001142}
1143
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001144static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1145{
1146 int tries;
1147
1148 for (tries = 0; tries < 10; ++tries) {
1149 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1150
1151 if (val & HDMI_PHY_STATUS_READY) {
1152 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1153 return;
1154 }
1155 usleep_range(10, 20);
1156 }
1157
1158 DRM_ERROR("PLL could not reach steady state\n");
1159}
1160
Rahul Sharma16844fb2013-06-10 14:50:00 +05301161static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001162{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001163 struct drm_display_mode *m = &hdata->current_mode;
1164 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001165
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001166 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1167 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1168 (m->htotal << 12) | m->vtotal);
1169
1170 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1171 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1172
1173 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1174 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1175
1176 val = (m->hsync_start - m->hdisplay - 2);
1177 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001178 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001179 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1180
1181 /*
1182 * Quirk requirement for exynos HDMI IP design,
1183 * 2 pixels less than the actual calculation for hsync_start
1184 * and end.
1185 */
1186
1187 /* Following values & calculations differ for different type of modes */
1188 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001189 val = ((m->vsync_end - m->vdisplay) / 2);
1190 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1191 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1192
1193 val = m->vtotal / 2;
1194 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1195 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1196
1197 val = (m->vtotal +
1198 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1199 val |= m->vtotal << 11;
1200 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1201
1202 val = ((m->vtotal / 2) + 7);
1203 val |= ((m->vtotal / 2) + 2) << 12;
1204 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1205
1206 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1207 val |= ((m->htotal / 2) +
1208 (m->hsync_start - m->hdisplay)) << 12;
1209 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1210
1211 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1212 (m->vtotal - m->vdisplay) / 2);
1213 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1214
1215 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1216 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001217 val = m->vtotal;
1218 val |= (m->vtotal - m->vdisplay) << 11;
1219 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1220
1221 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1222
1223 val = (m->vsync_end - m->vdisplay);
1224 val |= ((m->vsync_start - m->vdisplay) << 12);
1225 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1226
1227 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1228 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1229 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1230 m->vtotal - m->vdisplay);
1231 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001232 }
1233
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001234 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1235 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1236 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1237 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001238}
1239
Rahul Sharma16844fb2013-06-10 14:50:00 +05301240static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001241{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001242 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001243
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001244 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1245 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1246 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1247 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001248 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001249 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1250 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1251 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1252 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1253
1254 /*
1255 * Quirk requirement for exynos 5 HDMI IP design,
1256 * 2 pixels less than the actual calculation for hsync_start
1257 * and end.
1258 */
1259
1260 /* Following values & calculations differ for different type of modes */
1261 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001262 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1263 (m->vsync_end - m->vdisplay) / 2);
1264 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1265 (m->vsync_start - m->vdisplay) / 2);
1266 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1267 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1268 (m->vtotal - m->vdisplay) / 2);
1269 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1270 m->vtotal - m->vdisplay / 2);
1271 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1272 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1273 (m->vtotal / 2) + 7);
1274 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1275 (m->vtotal / 2) + 2);
1276 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1277 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1278 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1279 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1280 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1281 (m->vtotal - m->vdisplay) / 2);
1282 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1283 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1284 m->vtotal - m->vdisplay / 2);
1285 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1286 (m->vtotal / 2) + 1);
1287 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1288 (m->vtotal / 2) + 1);
1289 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1290 (m->vtotal / 2) + 1);
1291 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1292 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1293 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001294 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1295 m->vsync_end - m->vdisplay);
1296 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1297 m->vsync_start - m->vdisplay);
1298 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1299 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1300 m->vtotal - m->vdisplay);
1301 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1302 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1303 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1304 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1306 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1307 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1308 m->vtotal - m->vdisplay);
1309 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001310 }
1311
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001312 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1313 m->hsync_start - m->hdisplay - 2);
1314 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1315 m->hsync_end - m->hdisplay - 2);
1316 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1317 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1318 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1319 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1322 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1323 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1327 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1328 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1329 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1330 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1331 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1332 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1333 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001334
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001335 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1336 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1337 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1338 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001339 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1340 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001341}
1342
Rahul Sharma16844fb2013-06-10 14:50:00 +05301343static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001344{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001345 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301346 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001347 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301348 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001349
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001350 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001351}
1352
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001353static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1354{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001355 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1356 usleep_range(10000, 12000);
1357 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1358 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001359 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001360 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001361 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001362 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001363}
1364
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001365static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1366{
1367 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1368
1369 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1370 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1371}
1372
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001373static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1374{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001375 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001376 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001377
Andrzej Hajda4677f512016-03-23 14:15:12 +01001378 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1379 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001380 DRM_ERROR("failed to find hdmiphy conf\n");
1381 return;
1382 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001383 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1384
1385 hdmi_clk_set_parents(hdata, false);
1386
1387 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001388
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001389 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001390 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001391 if (ret) {
1392 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001393 return;
1394 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001395 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001396 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001397 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001398 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001399}
1400
1401static void hdmi_conf_apply(struct hdmi_context *hdata)
1402{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301403 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001404 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001405 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301406 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001407 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001408}
1409
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001410static void hdmi_mode_set(struct drm_encoder *encoder,
1411 struct drm_display_mode *mode,
1412 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001413{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001414 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001415 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001416
YoungJun Chocbc4c332013-06-12 10:44:40 +09001417 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1418 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001419 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001420 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001421
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001422 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001423}
1424
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001425static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1426{
1427 if (!hdata->sysreg)
1428 return;
1429
1430 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1431 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1432}
1433
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001434static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001435{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001436 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001437 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001438
Sean Paulaf65c802014-01-30 16:19:27 -05001439 pm_runtime_get_sync(hdata->dev);
1440
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001441 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001442 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1443
Rahul Sharma049d34e2014-05-20 10:36:05 +05301444 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1445 PMU_HDMI_PHY_ENABLE_BIT, 1);
1446
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001447 hdmi_set_refclk(hdata, true);
1448
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001449 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1450
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001451 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001452
1453 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001454}
1455
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001456static void hdmiphy_disable(struct hdmi_context *hdata)
1457{
1458 if (!hdata->powered)
1459 return;
1460
1461 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1462
1463 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1464
1465 hdmi_set_refclk(hdata, false);
1466
1467 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1468 PMU_HDMI_PHY_ENABLE_BIT, 0);
1469
1470 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1471
1472 pm_runtime_put_sync(hdata->dev);
1473
1474 hdata->powered = false;
1475}
1476
1477static void hdmi_enable(struct drm_encoder *encoder)
1478{
1479 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1480
1481 hdmiphy_enable(hdata);
1482 hdmi_conf_apply(hdata);
1483}
1484
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001485static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001486{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001487 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001488
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001489 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001490 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001491
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001492 /*
1493 * The SFRs of VP and Mixer are updated by Vertical Sync of
1494 * Timing generator which is a part of HDMI so the sequence
1495 * to disable TV Subsystem should be as following,
1496 * VP -> Mixer -> HDMI
1497 *
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001498 * To achieve such sequence HDMI is disabled together with HDMI PHY, via
1499 * pipe clock callback.
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001500 */
Sean Paul724fd142014-05-09 15:05:10 +09001501 cancel_delayed_work(&hdata->hotplug_work);
Andrzej Hajda625e63e2017-05-19 17:27:08 +09001502 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001503}
1504
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001505static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001506 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001507 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001508 .enable = hdmi_enable,
1509 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001510};
1511
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001512static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001513 .destroy = drm_encoder_cleanup,
1514};
1515
Sean Paul724fd142014-05-09 15:05:10 +09001516static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001517{
Sean Paul724fd142014-05-09 15:05:10 +09001518 struct hdmi_context *hdata;
1519
1520 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001521
Sean Paul45517892014-01-30 16:19:05 -05001522 if (hdata->drm_dev)
1523 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001524}
1525
1526static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1527{
1528 struct hdmi_context *hdata = arg;
1529
1530 mod_delayed_work(system_wq, &hdata->hotplug_work,
1531 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001532
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001533 return IRQ_HANDLED;
1534}
1535
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001536static int hdmi_clks_get(struct hdmi_context *hdata,
1537 const struct string_array_spec *names,
1538 struct clk **clks)
1539{
1540 struct device *dev = hdata->dev;
1541 int i;
1542
1543 for (i = 0; i < names->count; ++i) {
1544 struct clk *clk = devm_clk_get(dev, names->data[i]);
1545
1546 if (IS_ERR(clk)) {
1547 int ret = PTR_ERR(clk);
1548
1549 dev_err(dev, "Cannot get clock %s, %d\n",
1550 names->data[i], ret);
1551
1552 return ret;
1553 }
1554
1555 clks[i] = clk;
1556 }
1557
1558 return 0;
1559}
1560
1561static int hdmi_clk_init(struct hdmi_context *hdata)
1562{
1563 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1564 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1565 struct device *dev = hdata->dev;
1566 struct clk **clks;
1567 int ret;
1568
1569 if (!count)
1570 return 0;
1571
1572 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1573 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001574 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001575
1576 hdata->clk_gates = clks;
1577 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1578
1579 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1580 if (ret)
1581 return ret;
1582
1583 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1584}
1585
1586
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001587static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1588{
1589 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1590 phy_clk);
1591
1592 if (enable)
1593 hdmiphy_enable(hdata);
1594 else
1595 hdmiphy_disable(hdata);
1596}
1597
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001598static int hdmi_bridge_init(struct hdmi_context *hdata)
1599{
1600 struct device *dev = hdata->dev;
1601 struct device_node *ep, *np;
1602
1603 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1604 if (!ep)
1605 return 0;
1606
1607 np = of_graph_get_remote_port_parent(ep);
1608 of_node_put(ep);
1609 if (!np) {
1610 DRM_ERROR("failed to get remote port parent");
1611 return -EINVAL;
1612 }
1613
1614 hdata->bridge = of_drm_find_bridge(np);
1615 of_node_put(np);
1616
1617 if (!hdata->bridge)
1618 return -EPROBE_DEFER;
1619
1620 return 0;
1621}
1622
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001623static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001624{
1625 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001626 int i, ret;
1627
1628 DRM_DEBUG_KMS("HDMI resource init\n");
1629
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001630 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1631 if (IS_ERR(hdata->hpd_gpio)) {
1632 DRM_ERROR("cannot get hpd gpio property\n");
1633 return PTR_ERR(hdata->hpd_gpio);
1634 }
1635
1636 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1637 if (hdata->irq < 0) {
1638 DRM_ERROR("failed to get GPIO irq\n");
1639 return hdata->irq;
1640 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001641
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001642 ret = hdmi_clk_init(hdata);
1643 if (ret)
1644 return ret;
1645
1646 ret = hdmi_clk_set_parents(hdata, false);
1647 if (ret)
1648 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001649
Milo Kimc0d656d2016-08-31 15:14:27 +09001650 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001651 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001652
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001653 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001654 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001655 if (ret != -EPROBE_DEFER)
1656 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001657 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001658 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001659
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001660 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001661
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001662 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1663 if (IS_ERR(hdata->reg_hdmi_en))
1664 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001665
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001666 ret = regulator_enable(hdata->reg_hdmi_en);
1667 if (ret) {
1668 DRM_ERROR("failed to enable hdmi-en regulator\n");
1669 return ret;
1670 }
1671 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001672
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001673 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001674}
1675
Arvind Yadave3cc51e2017-06-19 14:54:03 +05301676static const struct of_device_id hdmi_match_types[] = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301677 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001678 .compatible = "samsung,exynos4210-hdmi",
1679 .data = &exynos4210_hdmi_driver_data,
1680 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301681 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001682 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301683 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301684 .compatible = "samsung,exynos5420-hdmi",
1685 .data = &exynos5420_hdmi_driver_data,
1686 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001687 .compatible = "samsung,exynos5433-hdmi",
1688 .data = &exynos5433_hdmi_driver_data,
1689 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301690 /* end node */
1691 }
1692};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001693MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301694
Inki Daef37cd5e2014-05-09 14:25:20 +09001695static int hdmi_bind(struct device *dev, struct device *master, void *data)
1696{
1697 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001698 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001699 struct drm_encoder *encoder = &hdata->encoder;
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001700 struct exynos_drm_crtc *crtc;
1701 int ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001702
Inki Daef37cd5e2014-05-09 14:25:20 +09001703 hdata->drm_dev = drm_dev;
1704
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001705 hdata->phy_clk.enable = hdmiphy_clk_enable;
1706
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001707 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001708 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001709
1710 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1711
Andrzej Hajda1ca582f2017-08-24 15:33:51 +02001712 ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_HDMI);
1713 if (ret < 0)
1714 return ret;
1715
1716 crtc = exynos_drm_crtc_get_by_type(drm_dev, EXYNOS_DISPLAY_TYPE_HDMI);
1717 crtc->pipe_clk = &hdata->phy_clk;
1718
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001719 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001720 if (ret) {
1721 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001722 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001723 return ret;
1724 }
1725
1726 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001727}
1728
1729static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1730{
Inki Daef37cd5e2014-05-09 14:25:20 +09001731}
1732
1733static const struct component_ops hdmi_component_ops = {
1734 .bind = hdmi_bind,
1735 .unbind = hdmi_unbind,
1736};
1737
Milo Kim1caa3602016-08-31 15:14:25 +09001738static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001739{
1740 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1741 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001742 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001743
1744 np = of_find_compatible_node(NULL, NULL, compatible_str);
1745 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001746 np = of_get_next_parent(np);
1747 else
1748 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001749
Milo Kim1caa3602016-08-31 15:14:25 +09001750 if (!np) {
1751 DRM_ERROR("Failed to find ddc node in device tree\n");
1752 return -ENODEV;
1753 }
1754
1755 adpt = of_find_i2c_adapter_by_node(np);
1756 of_node_put(np);
1757
1758 if (!adpt) {
1759 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1760 return -EPROBE_DEFER;
1761 }
1762
1763 hdata->ddc_adpt = adpt;
1764
1765 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001766}
1767
Milo Kimb5413022016-08-31 15:14:26 +09001768static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001769{
1770 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001771 struct device_node *np;
1772 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001773
Milo Kimb5413022016-08-31 15:14:26 +09001774 np = of_find_compatible_node(NULL, NULL, compatible_str);
1775 if (!np) {
1776 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1777 if (!np) {
1778 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1779 return -ENODEV;
1780 }
1781 }
1782
1783 if (hdata->drv_data->is_apb_phy) {
1784 hdata->regs_hdmiphy = of_iomap(np, 0);
1785 if (!hdata->regs_hdmiphy) {
1786 DRM_ERROR("failed to ioremap hdmi phy\n");
1787 ret = -ENOMEM;
1788 goto out;
1789 }
1790 } else {
1791 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1792 if (!hdata->hdmiphy_port) {
1793 DRM_INFO("Failed to get hdmi phy i2c client\n");
1794 ret = -EPROBE_DEFER;
1795 goto out;
1796 }
1797 }
1798
1799out:
1800 of_node_put(np);
1801 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001802}
1803
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001804static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001805{
1806 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001807 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001808 struct resource *res;
1809 int ret;
1810
Andrzej Hajda930865f2014-11-17 09:54:20 +01001811 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1812 if (!hdata)
1813 return -ENOMEM;
1814
Marek Szyprowski57a64122016-04-01 15:17:44 +02001815 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001816
Andrzej Hajda930865f2014-11-17 09:54:20 +01001817 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001818
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001819 hdata->dev = dev;
1820
1821 ret = hdmi_resources_init(hdata);
1822 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001823 if (ret != -EPROBE_DEFER)
1824 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001825 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001826 }
1827
1828 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001829 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001830 if (IS_ERR(hdata->regs)) {
1831 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001832 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001833 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001834
Milo Kim1caa3602016-08-31 15:14:25 +09001835 ret = hdmi_get_ddc_adapter(hdata);
1836 if (ret)
1837 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001838
Milo Kimb5413022016-08-31 15:14:26 +09001839 ret = hdmi_get_phy_io(hdata);
1840 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001841 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001842
Sean Paul724fd142014-05-09 15:05:10 +09001843 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1844
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001845 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001846 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001847 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001848 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001849 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001850 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001851 goto err_hdmiphy;
1852 }
1853
Rahul Sharma049d34e2014-05-20 10:36:05 +05301854 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1855 "samsung,syscon-phandle");
1856 if (IS_ERR(hdata->pmureg)) {
1857 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001858 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301859 goto err_hdmiphy;
1860 }
1861
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001862 if (hdata->drv_data->has_sysreg) {
1863 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1864 "samsung,sysreg-phandle");
1865 if (IS_ERR(hdata->sysreg)) {
1866 DRM_ERROR("sysreg regmap lookup failed.\n");
1867 ret = -EPROBE_DEFER;
1868 goto err_hdmiphy;
1869 }
1870 }
1871
Hans Verkuil278c8112016-12-13 11:07:17 -02001872 hdata->notifier = cec_notifier_get(&pdev->dev);
1873 if (hdata->notifier == NULL) {
1874 ret = -ENOMEM;
1875 goto err_hdmiphy;
1876 }
1877
Sean Paulaf65c802014-01-30 16:19:27 -05001878 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001879
Inki Daedf5225b2014-05-29 18:28:02 +09001880 ret = component_add(&pdev->dev, &hdmi_component_ops);
1881 if (ret)
Hans Verkuil278c8112016-12-13 11:07:17 -02001882 goto err_notifier_put;
Inki Daedf5225b2014-05-29 18:28:02 +09001883
1884 return ret;
1885
Hans Verkuil278c8112016-12-13 11:07:17 -02001886err_notifier_put:
1887 cec_notifier_put(hdata->notifier);
Inki Daedf5225b2014-05-29 18:28:02 +09001888 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001889
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001890err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001891 if (hdata->hdmiphy_port)
1892 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301893 if (hdata->regs_hdmiphy)
1894 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001895err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001896 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001897
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001898 return ret;
1899}
1900
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001901static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001902{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001903 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001904
Sean Paul724fd142014-05-09 15:05:10 +09001905 cancel_delayed_work_sync(&hdata->hotplug_work);
Hans Verkuil278c8112016-12-13 11:07:17 -02001906 cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
Sean Paul724fd142014-05-09 15:05:10 +09001907
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001908 component_del(&pdev->dev, &hdmi_component_ops);
1909
Hans Verkuil278c8112016-12-13 11:07:17 -02001910 cec_notifier_put(hdata->notifier);
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001911 pm_runtime_disable(&pdev->dev);
1912
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001913 if (!IS_ERR(hdata->reg_hdmi_en))
1914 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001915
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001916 if (hdata->hdmiphy_port)
1917 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001918
Arvind Yadavd7420002016-10-19 15:34:16 +05301919 if (hdata->regs_hdmiphy)
1920 iounmap(hdata->regs_hdmiphy);
1921
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001922 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001923
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001924 return 0;
1925}
1926
Arnd Bergmann7e175102017-07-21 22:47:18 +02001927static int __maybe_unused exynos_hdmi_suspend(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001928{
1929 struct hdmi_context *hdata = dev_get_drvdata(dev);
1930
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001931 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001932
1933 return 0;
1934}
1935
Arnd Bergmann7e175102017-07-21 22:47:18 +02001936static int __maybe_unused exynos_hdmi_resume(struct device *dev)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001937{
1938 struct hdmi_context *hdata = dev_get_drvdata(dev);
1939 int ret;
1940
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001941 ret = hdmi_clk_enable_gates(hdata);
1942 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001943 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001944
1945 return 0;
1946}
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001947
1948static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1949 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1950};
1951
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001952struct platform_driver hdmi_driver = {
1953 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001954 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001955 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301956 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001957 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001958 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301959 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001960 },
1961};