blob: 0f87acb4cf21020be72cd9e3366e3b7cdc3299ef [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>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053038#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090039#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053040#include <linux/mfd/syscon.h>
41#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090042
43#include <drm/exynos_drm.h>
44
45#include "exynos_drm_drv.h"
Inki Daef37cd5e2014-05-09 14:25:20 +090046#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090047
Sean Paul724fd142014-05-09 15:05:10 +090048#define HOTPLUG_DEBOUNCE_MS 1100
49
Rahul Sharmaa144c2e2012-11-26 10:52:57 +053050/* AVI header and aspect ratio */
51#define HDMI_AVI_VERSION 0x02
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090052#define HDMI_AVI_LENGTH 0x0d
Rahul Sharmaa144c2e2012-11-26 10:52:57 +053053
54/* AUI header info */
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090055#define HDMI_AUI_VERSION 0x01
56#define HDMI_AUI_LENGTH 0x0a
57
58/* AVI active format aspect ratio */
59#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x08
60#define AVI_4_3_CENTER_RATIO 0x09
61#define AVI_16_9_CENTER_RATIO 0x0a
Rahul Sharmaa144c2e2012-11-26 10:52:57 +053062
Rahul Sharma5a325072012-10-04 20:48:54 +053063enum hdmi_type {
64 HDMI_TYPE13,
65 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020066 HDMI_TYPE_COUNT
67};
68
69#define HDMI_MAPPED_BASE 0xffff0000
70
71enum hdmi_mapped_regs {
72 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
73 HDMI_PHY_RSTOUT,
74 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020075 HDMI_ACR_MCTS0,
76 HDMI_ACR_CTS0,
77 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020078};
79
80static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
81 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
82 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
83 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020084 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
85 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
86 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053087};
88
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020089static const char * const supply[] = {
90 "vdd",
91 "vdd_osc",
92 "vdd_pll",
93};
94
Andrzej Hajda65e98032015-11-02 14:16:41 +010095struct hdmiphy_config {
96 int pixel_clock;
97 u8 conf[32];
98};
99
100struct hdmiphy_configs {
101 int count;
102 const struct hdmiphy_config *data;
103};
104
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900105struct string_array_spec {
106 int count;
107 const char * const *data;
108};
109
110#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
111
Inki Daebfe4e842014-03-06 14:18:17 +0900112struct hdmi_driver_data {
113 unsigned int type;
114 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900115 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100116 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900117 struct string_array_spec clk_gates;
118 /*
119 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
120 * required parents of clock when HDMI-PHY is respectively off or on.
121 */
122 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900123};
124
Joonyoung Shim590f4182012-03-16 18:47:14 +0900125struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300126 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900127 struct device *dev;
128 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500129 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900130 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900131 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900132 struct delayed_work hotplug_work;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530133 struct drm_display_mode current_mode;
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200134 u8 cea_video_id;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200135 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900136
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200137 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900138 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200139 struct i2c_client *hdmiphy_port;
140 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900141 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200142 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530143 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900144 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900145 struct clk **clk_gates;
146 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200147 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
148 struct regulator *reg_hdmi_en;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900149};
150
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300151static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100152{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900153 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100154}
155
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200156static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
157{
158 return container_of(c, struct hdmi_context, connector);
159}
160
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900161static const struct hdmiphy_config hdmiphy_v13_configs[] = {
162 {
163 .pixel_clock = 27000000,
164 .conf = {
165 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
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 = 27027000,
173 .conf = {
174 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
175 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
176 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200177 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900178 },
179 },
180 {
181 .pixel_clock = 74176000,
182 .conf = {
183 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
184 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
185 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200186 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900187 },
188 },
189 {
190 .pixel_clock = 74250000,
191 .conf = {
192 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
193 0x6a, 0x10, 0x01, 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, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900196 },
197 },
198 {
199 .pixel_clock = 148500000,
200 .conf = {
201 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
202 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
203 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200204 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900205 },
206 },
207};
208
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500209static const struct hdmiphy_config hdmiphy_v14_configs[] = {
210 {
211 .pixel_clock = 25200000,
212 .conf = {
213 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
214 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
215 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
216 0x54, 0xf4, 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 = 27000000,
221 .conf = {
222 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
223 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
224 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
225 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
226 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900227 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500228 {
229 .pixel_clock = 27027000,
230 .conf = {
231 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
232 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
233 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200234 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500235 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900236 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500237 {
238 .pixel_clock = 36000000,
239 .conf = {
240 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
241 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
242 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
243 0x54, 0xab, 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 = 40000000,
248 .conf = {
249 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
250 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
251 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
252 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
253 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900254 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500255 {
256 .pixel_clock = 65000000,
257 .conf = {
258 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
259 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
260 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
261 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
262 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900263 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500264 {
Shirish Se1d883c2014-03-13 14:28:27 +0900265 .pixel_clock = 71000000,
266 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530267 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
268 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
269 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900270 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
271 },
272 },
273 {
274 .pixel_clock = 73250000,
275 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530276 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
277 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
278 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900279 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
280 },
281 },
282 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500283 .pixel_clock = 74176000,
284 .conf = {
285 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
286 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
287 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
288 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
289 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900290 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500291 {
292 .pixel_clock = 74250000,
293 .conf = {
294 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
295 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
296 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200297 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500298 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900299 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500300 {
301 .pixel_clock = 83500000,
302 .conf = {
303 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
304 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
305 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
306 0x54, 0x93, 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 = 106500000,
311 .conf = {
312 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
313 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
314 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
315 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
316 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900317 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500318 {
319 .pixel_clock = 108000000,
320 .conf = {
321 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
322 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
323 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
324 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
325 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900326 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500327 {
Shirish Se1d883c2014-03-13 14:28:27 +0900328 .pixel_clock = 115500000,
329 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530330 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
331 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
332 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900333 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
334 },
335 },
336 {
337 .pixel_clock = 119000000,
338 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530339 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
340 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
341 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900342 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
343 },
344 },
345 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500346 .pixel_clock = 146250000,
347 .conf = {
348 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
349 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
350 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
351 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
352 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900353 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500354 {
355 .pixel_clock = 148500000,
356 .conf = {
357 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
358 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
359 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200360 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500361 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900362 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900363};
364
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530365static const struct hdmiphy_config hdmiphy_5420_configs[] = {
366 {
367 .pixel_clock = 25200000,
368 .conf = {
369 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
370 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
371 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
372 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
373 },
374 },
375 {
376 .pixel_clock = 27000000,
377 .conf = {
378 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
379 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
380 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
381 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
382 },
383 },
384 {
385 .pixel_clock = 27027000,
386 .conf = {
387 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
388 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
389 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
390 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
391 },
392 },
393 {
394 .pixel_clock = 36000000,
395 .conf = {
396 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
397 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
398 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
399 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
400 },
401 },
402 {
403 .pixel_clock = 40000000,
404 .conf = {
405 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
406 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
407 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
408 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
409 },
410 },
411 {
412 .pixel_clock = 65000000,
413 .conf = {
414 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
415 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
416 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
417 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
418 },
419 },
420 {
421 .pixel_clock = 71000000,
422 .conf = {
423 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
424 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
425 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
426 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
427 },
428 },
429 {
430 .pixel_clock = 73250000,
431 .conf = {
432 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
433 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
434 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
435 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
436 },
437 },
438 {
439 .pixel_clock = 74176000,
440 .conf = {
441 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
442 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
443 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
444 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
445 },
446 },
447 {
448 .pixel_clock = 74250000,
449 .conf = {
450 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
451 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
452 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
453 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
454 },
455 },
456 {
457 .pixel_clock = 83500000,
458 .conf = {
459 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
460 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
461 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
462 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
463 },
464 },
465 {
466 .pixel_clock = 88750000,
467 .conf = {
468 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
469 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
470 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
471 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
472 },
473 },
474 {
475 .pixel_clock = 106500000,
476 .conf = {
477 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
478 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
479 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
480 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
481 },
482 },
483 {
484 .pixel_clock = 108000000,
485 .conf = {
486 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
487 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
488 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
489 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
490 },
491 },
492 {
493 .pixel_clock = 115500000,
494 .conf = {
495 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
496 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
497 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
498 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
499 },
500 },
501 {
502 .pixel_clock = 146250000,
503 .conf = {
504 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
505 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
506 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
507 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
508 },
509 },
510 {
511 .pixel_clock = 148500000,
512 .conf = {
513 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
514 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
515 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
516 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
517 },
518 },
519};
520
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900521static const struct hdmiphy_config hdmiphy_5433_configs[] = {
522 {
523 .pixel_clock = 27000000,
524 .conf = {
525 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46,
526 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
527 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
528 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
529 },
530 },
531 {
532 .pixel_clock = 27027000,
533 .conf = {
534 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
535 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5,
536 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
537 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
538 },
539 },
540 {
541 .pixel_clock = 40000000,
542 .conf = {
543 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
544 0x4d, 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 = 50000000,
551 .conf = {
552 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
553 0x3d, 0x50, 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 = 65000000,
560 .conf = {
561 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
562 0x2e, 0xe8, 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 = 74176000,
569 .conf = {
570 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
571 0x53, 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 = 74250000,
578 .conf = {
579 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
580 0x52, 0x51, 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 = 108000000,
587 .conf = {
588 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
589 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
590 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
591 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
592 },
593 },
594 {
595 .pixel_clock = 148500000,
596 .conf = {
597 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
598 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
599 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
600 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
601 },
602 },
603};
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
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900683static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
684 u32 reg_id, u32 value, u32 mask)
685{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200686 u32 old;
687
688 reg_id = hdmi_map_reg(hdata, reg_id);
689 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900690 value = (value & mask) | (old & ~mask);
691 writel(value, hdata->regs + reg_id);
692}
693
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900694static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
695 u32 reg_offset, const u8 *buf, u32 len)
696{
697 if ((reg_offset + len) > 32)
698 return -EINVAL;
699
700 if (hdata->hdmiphy_port) {
701 int ret;
702
703 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
704 if (ret == len)
705 return 0;
706 return ret;
707 } else {
708 int i;
709 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200710 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900711 ((reg_offset + i)<<2));
712 return 0;
713 }
714}
715
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900716static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
717{
718 int i, ret;
719
720 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
721 ret = clk_prepare_enable(hdata->clk_gates[i]);
722 if (!ret)
723 continue;
724
725 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
726 hdata->drv_data->clk_gates.data[i], ret);
727 while (i--)
728 clk_disable_unprepare(hdata->clk_gates[i]);
729 return ret;
730 }
731
732 return 0;
733}
734
735static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
736{
737 int i = hdata->drv_data->clk_gates.count;
738
739 while (i--)
740 clk_disable_unprepare(hdata->clk_gates[i]);
741}
742
743static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
744{
745 struct device *dev = hdata->dev;
746 int ret = 0;
747 int i;
748
749 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
750 struct clk **c = &hdata->clk_muxes[i];
751
752 ret = clk_set_parent(c[2], c[to_phy]);
753 if (!ret)
754 continue;
755
756 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
757 hdata->drv_data->clk_muxes.data[i + 2],
758 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
759 }
760
761 return ret;
762}
763
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530764static u8 hdmi_chksum(struct hdmi_context *hdata,
765 u32 start, u8 len, u32 hdr_sum)
766{
767 int i;
768
769 /* hdr_sum : header0 + header1 + header2
770 * start : start address of packet byte1
771 * len : packet bytes - 1 */
772 for (i = 0; i < len; ++i)
773 hdr_sum += 0xff & hdmi_reg_read(hdata, start + i * 4);
774
775 /* return 2's complement of 8 bit hdr_sum */
776 return (u8)(~(hdr_sum & 0xff) + 1);
777}
778
779static void hdmi_reg_infoframe(struct hdmi_context *hdata,
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530780 union hdmi_infoframe *infoframe)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530781{
782 u32 hdr_sum;
783 u8 chksum;
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200784 u8 ar;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530785
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530786 if (hdata->dvi_mode) {
787 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
788 HDMI_VSI_CON_DO_NOT_TRANSMIT);
789 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
790 HDMI_AVI_CON_DO_NOT_TRANSMIT);
791 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
792 return;
793 }
794
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530795 switch (infoframe->any.type) {
796 case HDMI_INFOFRAME_TYPE_AVI:
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530797 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530798 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER0, infoframe->any.type);
799 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER1,
800 infoframe->any.version);
801 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER2, infoframe->any.length);
802 hdr_sum = infoframe->any.type + infoframe->any.version +
803 infoframe->any.length;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530804
805 /* Output format zero hardcoded ,RGB YBCR selection */
806 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 0 << 5 |
807 AVI_ACTIVE_FORMAT_VALID |
808 AVI_UNDERSCANNED_DISPLAY_VALID);
809
Shirish S46154152014-03-13 10:58:28 +0530810 /*
811 * Set the aspect ratio as per the mode, mentioned in
812 * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard
813 */
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200814 ar = hdata->current_mode.picture_aspect_ratio;
815 switch (ar) {
Shirish S46154152014-03-13 10:58:28 +0530816 case HDMI_PICTURE_ASPECT_4_3:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200817 ar |= AVI_4_3_CENTER_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530818 break;
819 case HDMI_PICTURE_ASPECT_16_9:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200820 ar |= AVI_16_9_CENTER_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530821 break;
822 case HDMI_PICTURE_ASPECT_NONE:
823 default:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200824 ar |= AVI_SAME_AS_PIC_ASPECT_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530825 break;
826 }
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200827 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), ar);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530828
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200829 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), hdata->cea_video_id);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530830
831 chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1),
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530832 infoframe->any.length, hdr_sum);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530833 DRM_DEBUG_KMS("AVI checksum = 0x%x\n", chksum);
834 hdmi_reg_writeb(hdata, HDMI_AVI_CHECK_SUM, chksum);
835 break;
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530836 case HDMI_INFOFRAME_TYPE_AUDIO:
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530837 hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530838 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER0, infoframe->any.type);
839 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER1,
840 infoframe->any.version);
841 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER2, infoframe->any.length);
842 hdr_sum = infoframe->any.type + infoframe->any.version +
843 infoframe->any.length;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530844 chksum = hdmi_chksum(hdata, HDMI_AUI_BYTE(1),
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530845 infoframe->any.length, hdr_sum);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530846 DRM_DEBUG_KMS("AUI checksum = 0x%x\n", chksum);
847 hdmi_reg_writeb(hdata, HDMI_AUI_CHECK_SUM, chksum);
848 break;
849 default:
850 break;
851 }
852}
853
Sean Pauld9716ee2014-01-30 16:19:29 -0500854static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
855 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500856{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200857 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500858
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200859 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200860 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530861
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200862 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500863}
864
Sean Pauld9716ee2014-01-30 16:19:29 -0500865static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900866{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200867 drm_connector_unregister(connector);
868 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900869}
870
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100871static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300872 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500873 .fill_modes = drm_helper_probe_single_connector_modes,
874 .detect = hdmi_detect,
875 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300876 .reset = drm_atomic_helper_connector_reset,
877 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
878 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500879};
880
881static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900882{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200883 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500884 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200885 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900886
Inki Dae8fa04aa2014-03-13 16:38:31 +0900887 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500888 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900889
Inki Dae8fa04aa2014-03-13 16:38:31 +0900890 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500891 if (!edid)
892 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900893
Sean Pauld9716ee2014-01-30 16:19:29 -0500894 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500895 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
896 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500897 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500898
Sean Pauld9716ee2014-01-30 16:19:29 -0500899 drm_mode_connector_update_edid_property(connector, edid);
900
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200901 ret = drm_add_edid_modes(connector, edid);
902
903 kfree(edid);
904
905 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900906}
907
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900908static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900909{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100910 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900911 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900912
Andrzej Hajda65e98032015-11-02 14:16:41 +0100913 for (i = 0; i < confs->count; i++)
914 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500915 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500916
917 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
918 return -EINVAL;
919}
920
Sean Pauld9716ee2014-01-30 16:19:29 -0500921static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500922 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900923{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200924 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900925 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900926
Rahul Sharma16844fb2013-06-10 14:50:00 +0530927 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
928 mode->hdisplay, mode->vdisplay, mode->vrefresh,
929 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
930 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900931
Rahul Sharma16844fb2013-06-10 14:50:00 +0530932 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900933 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500934 return MODE_BAD;
935
936 return MODE_OK;
937}
938
939static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
940{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200941 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500942
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300943 return &hdata->encoder;
Sean Pauld9716ee2014-01-30 16:19:29 -0500944}
945
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100946static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500947 .get_modes = hdmi_get_modes,
948 .mode_valid = hdmi_mode_valid,
949 .best_encoder = hdmi_best_encoder,
950};
951
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300952static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500953{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300954 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500955 struct drm_connector *connector = &hdata->connector;
956 int ret;
957
Sean Pauld9716ee2014-01-30 16:19:29 -0500958 connector->interlace_allowed = true;
959 connector->polled = DRM_CONNECTOR_POLL_HPD;
960
961 ret = drm_connector_init(hdata->drm_dev, connector,
962 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
963 if (ret) {
964 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900965 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500966 }
967
968 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100969 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300970 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500971
972 return 0;
973}
974
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300975static bool hdmi_mode_fixup(struct drm_encoder *encoder,
976 const struct drm_display_mode *mode,
977 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500978{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300979 struct drm_device *dev = encoder->dev;
980 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500981 struct drm_display_mode *m;
982 int mode_ok;
983
Sean Paulf041b252014-01-30 16:19:15 -0500984 drm_mode_set_crtcinfo(adjusted_mode, 0);
985
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300986 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
987 if (connector->encoder == encoder)
988 break;
989 }
990
991 if (connector->encoder != encoder)
992 return true;
993
Sean Pauld9716ee2014-01-30 16:19:29 -0500994 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500995
Sean Pauld9716ee2014-01-30 16:19:29 -0500996 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300997 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500998
999 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001000 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -05001001 */
1002 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -05001003 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -05001004
Sean Pauld9716ee2014-01-30 16:19:29 -05001005 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -05001006 DRM_INFO("desired mode doesn't exist so\n");
1007 DRM_INFO("use the most suitable mode among modes.\n");
1008
1009 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
1010 m->hdisplay, m->vdisplay, m->vrefresh);
1011
Sean Paul75626852014-01-30 16:19:16 -05001012 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -05001013 break;
1014 }
1015 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001016
1017 return true;
Sean Paulf041b252014-01-30 16:19:15 -05001018}
1019
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001020static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001021{
1022 u32 n, cts;
1023
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001024 cts = (freq % 9) ? 27000 : 30000;
1025 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001026
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001027 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
1028 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
1029 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001030 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001031}
1032
1033static void hdmi_audio_init(struct hdmi_context *hdata)
1034{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301035 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001036 u32 data_num, bit_ch, sample_frq;
1037 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001038
1039 sample_rate = 44100;
1040 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001041
1042 switch (bits_per_sample) {
1043 case 20:
1044 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001045 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001046 break;
1047 case 24:
1048 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001049 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001050 break;
1051 default:
1052 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001053 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001054 break;
1055 }
1056
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001057 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001058
1059 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1060 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1061 | HDMI_I2S_MUX_ENABLE);
1062
1063 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1064 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1065
1066 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1067
1068 sample_frq = (sample_rate == 44100) ? 0 :
1069 (sample_rate == 48000) ? 2 :
1070 (sample_rate == 32000) ? 3 :
1071 (sample_rate == 96000) ? 0xa : 0x0;
1072
1073 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1074 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1075
1076 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1077 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1078
1079 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1080 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1081 | HDMI_I2S_SEL_LRCK(6));
1082 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1083 | HDMI_I2S_SEL_SDATA2(4));
1084 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1085 | HDMI_I2S_SEL_SDATA2(2));
1086 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1087
1088 /* I2S_CON_1 & 2 */
1089 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1090 | HDMI_I2S_L_CH_LOW_POL);
1091 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1092 | HDMI_I2S_SET_BIT_CH(bit_ch)
1093 | HDMI_I2S_SET_SDATA_BIT(data_num)
1094 | HDMI_I2S_BASIC_FORMAT);
1095
1096 /* Configure register related to CUV information */
1097 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1098 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1099 | HDMI_I2S_COPYRIGHT
1100 | HDMI_I2S_LINEAR_PCM
1101 | HDMI_I2S_CONSUMER_FORMAT);
1102 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1103 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1104 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1105 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1106 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1107 HDMI_I2S_ORG_SMP_FREQ_44_1
1108 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1109 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1110
1111 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1112}
1113
1114static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1115{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001116 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001117 return;
1118
1119 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1120 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1121 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1122}
1123
Rahul Sharmabfa48422014-04-03 20:41:04 +05301124static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001125{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301126 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001127
Rahul Sharmabfa48422014-04-03 20:41:04 +05301128 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1129 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001130
Rahul Sharmabfa48422014-04-03 20:41:04 +05301131 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1132 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001133}
1134
1135static void hdmi_conf_init(struct hdmi_context *hdata)
1136{
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301137 union hdmi_infoframe infoframe;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301138
Sean Paul77006a72013-01-16 10:17:20 -05001139 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001140 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1141 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001142
1143 /* choose HDMI mode */
1144 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1145 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001146 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301147 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001148 /* disable bluescreen */
1149 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001150
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001151 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001152 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1153 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1154 hdmi_reg_writeb(hdata, HDMI_CON_2,
1155 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1156 }
1157
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001158 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001159 /* choose bluescreen (fecal) color */
1160 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1161 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1162 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1163
1164 /* enable AVI packet every vsync, fixes purple line problem */
1165 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1166 /* force RGB, look to CEA-861-D, table 7 for more detail */
1167 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1168 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1169
1170 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1171 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1172 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1173 } else {
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301174 infoframe.any.type = HDMI_INFOFRAME_TYPE_AVI;
1175 infoframe.any.version = HDMI_AVI_VERSION;
1176 infoframe.any.length = HDMI_AVI_LENGTH;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301177 hdmi_reg_infoframe(hdata, &infoframe);
1178
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301179 infoframe.any.type = HDMI_INFOFRAME_TYPE_AUDIO;
1180 infoframe.any.version = HDMI_AUI_VERSION;
1181 infoframe.any.length = HDMI_AUI_LENGTH;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301182 hdmi_reg_infoframe(hdata, &infoframe);
1183
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001184 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001185 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1186 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001187}
1188
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001189static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1190{
1191 int tries;
1192
1193 for (tries = 0; tries < 10; ++tries) {
1194 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1195
1196 if (val & HDMI_PHY_STATUS_READY) {
1197 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1198 return;
1199 }
1200 usleep_range(10, 20);
1201 }
1202
1203 DRM_ERROR("PLL could not reach steady state\n");
1204}
1205
Rahul Sharma16844fb2013-06-10 14:50:00 +05301206static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001207{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001208 struct drm_display_mode *m = &hdata->current_mode;
1209 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001210
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001211 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1212 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1213 (m->htotal << 12) | m->vtotal);
1214
1215 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1216 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1217
1218 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1219 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1220
1221 val = (m->hsync_start - m->hdisplay - 2);
1222 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001223 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001224 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1225
1226 /*
1227 * Quirk requirement for exynos HDMI IP design,
1228 * 2 pixels less than the actual calculation for hsync_start
1229 * and end.
1230 */
1231
1232 /* Following values & calculations differ for different type of modes */
1233 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001234 val = ((m->vsync_end - m->vdisplay) / 2);
1235 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1236 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1237
1238 val = m->vtotal / 2;
1239 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1240 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1241
1242 val = (m->vtotal +
1243 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1244 val |= m->vtotal << 11;
1245 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1246
1247 val = ((m->vtotal / 2) + 7);
1248 val |= ((m->vtotal / 2) + 2) << 12;
1249 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1250
1251 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1252 val |= ((m->htotal / 2) +
1253 (m->hsync_start - m->hdisplay)) << 12;
1254 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1255
1256 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1257 (m->vtotal - m->vdisplay) / 2);
1258 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1259
1260 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1261 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001262 val = m->vtotal;
1263 val |= (m->vtotal - m->vdisplay) << 11;
1264 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1265
1266 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1267
1268 val = (m->vsync_end - m->vdisplay);
1269 val |= ((m->vsync_start - m->vdisplay) << 12);
1270 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1271
1272 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1273 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1274 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1275 m->vtotal - m->vdisplay);
1276 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001277 }
1278
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001279 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1280 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1281 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1282 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001283}
1284
Rahul Sharma16844fb2013-06-10 14:50:00 +05301285static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001286{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001287 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001288
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001289 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1290 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1291 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1292 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001293 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001294 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1295 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1296 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1297 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1298
1299 /*
1300 * Quirk requirement for exynos 5 HDMI IP design,
1301 * 2 pixels less than the actual calculation for hsync_start
1302 * and end.
1303 */
1304
1305 /* Following values & calculations differ for different type of modes */
1306 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001307 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1308 (m->vsync_end - m->vdisplay) / 2);
1309 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1310 (m->vsync_start - m->vdisplay) / 2);
1311 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1312 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1313 (m->vtotal - m->vdisplay) / 2);
1314 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1315 m->vtotal - m->vdisplay / 2);
1316 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1317 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1318 (m->vtotal / 2) + 7);
1319 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1320 (m->vtotal / 2) + 2);
1321 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1322 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1323 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1324 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1325 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1326 (m->vtotal - m->vdisplay) / 2);
1327 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1328 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1329 m->vtotal - m->vdisplay / 2);
1330 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1331 (m->vtotal / 2) + 1);
1332 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1333 (m->vtotal / 2) + 1);
1334 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1335 (m->vtotal / 2) + 1);
1336 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1337 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1338 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001339 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1340 m->vsync_end - m->vdisplay);
1341 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1342 m->vsync_start - m->vdisplay);
1343 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1344 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1345 m->vtotal - m->vdisplay);
1346 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1347 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1348 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1349 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1350 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1351 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1352 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1353 m->vtotal - m->vdisplay);
1354 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001355 }
1356
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001357 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1358 m->hsync_start - m->hdisplay - 2);
1359 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1360 m->hsync_end - m->hdisplay - 2);
1361 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1362 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1363 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1364 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1365 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1366 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1367 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1368 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1369 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1370 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1371 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1372 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1373 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1374 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1375 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1376 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1377 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1378 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001379
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001380 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1381 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1382 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1383 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001384 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1385 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001386}
1387
Rahul Sharma16844fb2013-06-10 14:50:00 +05301388static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001389{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001390 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301391 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001392 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301393 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001394
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001395 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001396}
1397
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001398static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1399{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001400 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1401 usleep_range(10000, 12000);
1402 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1403 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001404 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001405 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001406 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001407 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001408}
1409
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001410static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1411{
1412 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1413
1414 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1415 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1416}
1417
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001418static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1419{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001420 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001421 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001422
Andrzej Hajda4677f512016-03-23 14:15:12 +01001423 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1424 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001425 DRM_ERROR("failed to find hdmiphy conf\n");
1426 return;
1427 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001428 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1429
1430 hdmi_clk_set_parents(hdata, false);
1431
1432 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001433
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001434 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001435 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001436 if (ret) {
1437 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001438 return;
1439 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001440 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001441 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001442 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001443 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001444}
1445
1446static void hdmi_conf_apply(struct hdmi_context *hdata)
1447{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001448 hdmiphy_conf_apply(hdata);
Rahul Sharmabfa48422014-04-03 20:41:04 +05301449 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001450 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001451 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301452 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001453 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001454}
1455
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001456static void hdmi_mode_set(struct drm_encoder *encoder,
1457 struct drm_display_mode *mode,
1458 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001459{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001460 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001461 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001462
YoungJun Chocbc4c332013-06-12 10:44:40 +09001463 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1464 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001465 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001466 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001467
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001468 drm_mode_copy(&hdata->current_mode, m);
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +02001469 hdata->cea_video_id = drm_match_cea_mode(mode);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001470}
1471
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001472static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1473{
1474 if (!hdata->sysreg)
1475 return;
1476
1477 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1478 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1479}
1480
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001481static void hdmi_enable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001482{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001483 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001484
Andrzej Hajda882a0642015-07-09 16:28:08 +02001485 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001486 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001487
Sean Paulaf65c802014-01-30 16:19:27 -05001488 pm_runtime_get_sync(hdata->dev);
1489
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001490 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001491 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1492
Rahul Sharma049d34e2014-05-20 10:36:05 +05301493 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1494 PMU_HDMI_PHY_ENABLE_BIT, 1);
1495
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001496 hdmi_set_refclk(hdata, true);
1497
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001498 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1499
Gustavo Padovanc2c099f2015-08-05 20:24:17 -03001500 hdmi_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001501
1502 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001503}
1504
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001505static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001506{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001507 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001508 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001509 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001510
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001511 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001512 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001513
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001514 /*
1515 * The SFRs of VP and Mixer are updated by Vertical Sync of
1516 * Timing generator which is a part of HDMI so the sequence
1517 * to disable TV Subsystem should be as following,
1518 * VP -> Mixer -> HDMI
1519 *
1520 * Below codes will try to disable Mixer and VP(if used)
1521 * prior to disabling HDMI.
1522 */
1523 if (crtc)
1524 funcs = crtc->helper_private;
1525 if (funcs && funcs->disable)
1526 (*funcs->disable)(crtc);
1527
Rahul Sharmabfa48422014-04-03 20:41:04 +05301528 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1529
Sean Paul724fd142014-05-09 15:05:10 +09001530 cancel_delayed_work(&hdata->hotplug_work);
1531
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001532 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1533
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001534 hdmi_set_refclk(hdata, false);
1535
Rahul Sharma049d34e2014-05-20 10:36:05 +05301536 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1537 PMU_HDMI_PHY_ENABLE_BIT, 0);
1538
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001539 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001540
Sean Paulaf65c802014-01-30 16:19:27 -05001541 pm_runtime_put_sync(hdata->dev);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001542
1543 hdata->powered = false;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001544}
1545
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001546static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001547 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001548 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001549 .enable = hdmi_enable,
1550 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001551};
1552
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001553static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001554 .destroy = drm_encoder_cleanup,
1555};
1556
Sean Paul724fd142014-05-09 15:05:10 +09001557static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001558{
Sean Paul724fd142014-05-09 15:05:10 +09001559 struct hdmi_context *hdata;
1560
1561 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001562
Sean Paul45517892014-01-30 16:19:05 -05001563 if (hdata->drm_dev)
1564 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001565}
1566
1567static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1568{
1569 struct hdmi_context *hdata = arg;
1570
1571 mod_delayed_work(system_wq, &hdata->hotplug_work,
1572 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001573
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001574 return IRQ_HANDLED;
1575}
1576
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001577static int hdmi_clks_get(struct hdmi_context *hdata,
1578 const struct string_array_spec *names,
1579 struct clk **clks)
1580{
1581 struct device *dev = hdata->dev;
1582 int i;
1583
1584 for (i = 0; i < names->count; ++i) {
1585 struct clk *clk = devm_clk_get(dev, names->data[i]);
1586
1587 if (IS_ERR(clk)) {
1588 int ret = PTR_ERR(clk);
1589
1590 dev_err(dev, "Cannot get clock %s, %d\n",
1591 names->data[i], ret);
1592
1593 return ret;
1594 }
1595
1596 clks[i] = clk;
1597 }
1598
1599 return 0;
1600}
1601
1602static int hdmi_clk_init(struct hdmi_context *hdata)
1603{
1604 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1605 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1606 struct device *dev = hdata->dev;
1607 struct clk **clks;
1608 int ret;
1609
1610 if (!count)
1611 return 0;
1612
1613 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1614 if (!clks)
1615 return -ENOMEM;
1616
1617 hdata->clk_gates = clks;
1618 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1619
1620 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1621 if (ret)
1622 return ret;
1623
1624 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1625}
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
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001655 for (i = 0; i < ARRAY_SIZE(supply); ++i) {
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001656 hdata->regul_bulk[i].supply = supply[i];
1657 hdata->regul_bulk[i].consumer = NULL;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001658 }
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001659 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001660 if (ret) {
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 Hajdaaf1f7c22015-09-25 14:48:25 +02001667 if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001668 return 0;
1669
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001670 if (IS_ERR(hdata->reg_hdmi_en))
1671 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001672
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001673 ret = regulator_enable(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001674 if (ret)
1675 DRM_ERROR("failed to enable hdmi-en regulator\n");
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001676
Inki Daedf5225b2014-05-29 18:28:02 +09001677 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001678}
1679
Rahul Sharma22c4f422012-10-04 20:48:55 +05301680static struct of_device_id hdmi_match_types[] = {
1681 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001682 .compatible = "samsung,exynos4210-hdmi",
1683 .data = &exynos4210_hdmi_driver_data,
1684 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301685 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001686 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301687 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301688 .compatible = "samsung,exynos5420-hdmi",
1689 .data = &exynos5420_hdmi_driver_data,
1690 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001691 .compatible = "samsung,exynos5433-hdmi",
1692 .data = &exynos5433_hdmi_driver_data,
1693 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301694 /* end node */
1695 }
1696};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001697MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301698
Inki Daef37cd5e2014-05-09 14:25:20 +09001699static int hdmi_bind(struct device *dev, struct device *master, void *data)
1700{
1701 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001702 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001703 struct drm_encoder *encoder = &hdata->encoder;
1704 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001705
Inki Daef37cd5e2014-05-09 14:25:20 +09001706 hdata->drm_dev = drm_dev;
1707
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001708 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1709 EXYNOS_DISPLAY_TYPE_HDMI);
1710 if (pipe < 0)
1711 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001712
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001713 encoder->possible_crtcs = 1 << pipe;
1714
1715 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1716
1717 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001718 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001719
1720 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1721
1722 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001723 if (ret) {
1724 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001725 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001726 return ret;
1727 }
1728
1729 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001730}
1731
1732static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1733{
Inki Daef37cd5e2014-05-09 14:25:20 +09001734}
1735
1736static const struct component_ops hdmi_component_ops = {
1737 .bind = hdmi_bind,
1738 .unbind = hdmi_unbind,
1739};
1740
Inki Daee2a562d2014-05-09 16:46:10 +09001741static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
1742{
1743 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1744 struct device_node *np;
1745
1746 np = of_find_compatible_node(NULL, NULL, compatible_str);
1747 if (np)
1748 return of_get_next_parent(np);
1749
1750 return NULL;
1751}
1752
1753static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
1754{
1755 const char *compatible_str = "samsung,exynos4212-hdmiphy";
1756
1757 return of_find_compatible_node(NULL, NULL, compatible_str);
1758}
1759
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001760static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001761{
Inki Daef37cd5e2014-05-09 14:25:20 +09001762 struct device_node *ddc_node, *phy_node;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001763 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001764 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001765 struct resource *res;
1766 int ret;
1767
Andrzej Hajda930865f2014-11-17 09:54:20 +01001768 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1769 if (!hdata)
1770 return -ENOMEM;
1771
Marek Szyprowski57a64122016-04-01 15:17:44 +02001772 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001773
Andrzej Hajda930865f2014-11-17 09:54:20 +01001774 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001775
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001776 hdata->dev = dev;
1777
1778 ret = hdmi_resources_init(hdata);
1779 if (ret) {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301780 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001781 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001782 }
1783
1784 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001785 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001786 if (IS_ERR(hdata->regs)) {
1787 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001788 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001789 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001790
Inki Daee2a562d2014-05-09 16:46:10 +09001791 ddc_node = hdmi_legacy_ddc_dt_binding(dev);
1792 if (ddc_node)
1793 goto out_get_ddc_adpt;
1794
Daniel Kurtz2b768132014-02-24 18:52:51 +09001795 ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
1796 if (!ddc_node) {
1797 DRM_ERROR("Failed to find ddc node in device tree\n");
Andrzej Hajda86650402015-06-11 23:23:37 +09001798 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001799 }
Inki Daee2a562d2014-05-09 16:46:10 +09001800
1801out_get_ddc_adpt:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001802 hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
1803 if (!hdata->ddc_adpt) {
1804 DRM_ERROR("Failed to get ddc i2c adapter by node\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001805 return -EPROBE_DEFER;
Daniel Kurtz2b768132014-02-24 18:52:51 +09001806 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001807
Inki Daee2a562d2014-05-09 16:46:10 +09001808 phy_node = hdmi_legacy_phy_dt_binding(dev);
1809 if (phy_node)
1810 goto out_get_phy_port;
1811
Daniel Kurtz2b768132014-02-24 18:52:51 +09001812 phy_node = of_parse_phandle(dev->of_node, "phy", 0);
1813 if (!phy_node) {
1814 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1815 ret = -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001816 goto err_ddc;
1817 }
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001818
Inki Daee2a562d2014-05-09 16:46:10 +09001819out_get_phy_port:
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001820 if (hdata->drv_data->is_apb_phy) {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001821 hdata->regs_hdmiphy = of_iomap(phy_node, 0);
1822 if (!hdata->regs_hdmiphy) {
1823 DRM_ERROR("failed to ioremap hdmi phy\n");
1824 ret = -ENOMEM;
1825 goto err_ddc;
1826 }
1827 } else {
1828 hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
1829 if (!hdata->hdmiphy_port) {
1830 DRM_ERROR("Failed to get hdmi phy i2c client\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001831 ret = -EPROBE_DEFER;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001832 goto err_ddc;
1833 }
Daniel Kurtz2b768132014-02-24 18:52:51 +09001834 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001835
Sean Paul724fd142014-05-09 15:05:10 +09001836 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1837
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001838 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001839 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001840 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001841 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001842 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001843 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001844 goto err_hdmiphy;
1845 }
1846
Rahul Sharma049d34e2014-05-20 10:36:05 +05301847 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1848 "samsung,syscon-phandle");
1849 if (IS_ERR(hdata->pmureg)) {
1850 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001851 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301852 goto err_hdmiphy;
1853 }
1854
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001855 if (hdata->drv_data->has_sysreg) {
1856 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1857 "samsung,sysreg-phandle");
1858 if (IS_ERR(hdata->sysreg)) {
1859 DRM_ERROR("sysreg regmap lookup failed.\n");
1860 ret = -EPROBE_DEFER;
1861 goto err_hdmiphy;
1862 }
1863 }
1864
Sean Paulaf65c802014-01-30 16:19:27 -05001865 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001866
Inki Daedf5225b2014-05-29 18:28:02 +09001867 ret = component_add(&pdev->dev, &hdmi_component_ops);
1868 if (ret)
1869 goto err_disable_pm_runtime;
1870
1871 return ret;
1872
1873err_disable_pm_runtime:
1874 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001875
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001876err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001877 if (hdata->hdmiphy_port)
1878 put_device(&hdata->hdmiphy_port->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001879err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001880 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001881
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001882 return ret;
1883}
1884
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001885static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001886{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001887 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001888
Sean Paul724fd142014-05-09 15:05:10 +09001889 cancel_delayed_work_sync(&hdata->hotplug_work);
1890
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001891 component_del(&pdev->dev, &hdmi_component_ops);
1892
1893 pm_runtime_disable(&pdev->dev);
1894
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001895 if (!IS_ERR(hdata->reg_hdmi_en))
1896 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001897
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001898 if (hdata->hdmiphy_port)
1899 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001900
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001901 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001902
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001903 return 0;
1904}
1905
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001906#ifdef CONFIG_PM
1907static int exynos_hdmi_suspend(struct device *dev)
1908{
1909 struct hdmi_context *hdata = dev_get_drvdata(dev);
1910
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001911 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001912
1913 return 0;
1914}
1915
1916static int exynos_hdmi_resume(struct device *dev)
1917{
1918 struct hdmi_context *hdata = dev_get_drvdata(dev);
1919 int ret;
1920
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001921 ret = hdmi_clk_enable_gates(hdata);
1922 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001923 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001924
1925 return 0;
1926}
1927#endif
1928
1929static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1930 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1931};
1932
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001933struct platform_driver hdmi_driver = {
1934 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001935 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001936 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301937 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001938 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001939 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301940 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001941 },
1942};