blob: 38eaa63afb31f61125314893d154ec03171cb3e1 [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 Kim872d20d2012-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;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900149 struct exynos_drm_clk phy_clk;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900150};
151
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300152static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100153{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900154 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100155}
156
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200157static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
158{
159 return container_of(c, struct hdmi_context, connector);
160}
161
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900162static const struct hdmiphy_config hdmiphy_v13_configs[] = {
163 {
164 .pixel_clock = 27000000,
165 .conf = {
166 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
167 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
168 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200169 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900170 },
171 },
172 {
173 .pixel_clock = 27027000,
174 .conf = {
175 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
176 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
177 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200178 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900179 },
180 },
181 {
182 .pixel_clock = 74176000,
183 .conf = {
184 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
185 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
186 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200187 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900188 },
189 },
190 {
191 .pixel_clock = 74250000,
192 .conf = {
193 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
194 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
195 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200196 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900197 },
198 },
199 {
200 .pixel_clock = 148500000,
201 .conf = {
202 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
203 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
204 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200205 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900206 },
207 },
208};
209
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500210static const struct hdmiphy_config hdmiphy_v14_configs[] = {
211 {
212 .pixel_clock = 25200000,
213 .conf = {
214 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
215 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
216 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
217 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
218 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900219 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500220 {
221 .pixel_clock = 27000000,
222 .conf = {
223 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
224 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
225 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
226 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
227 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900228 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500229 {
230 .pixel_clock = 27027000,
231 .conf = {
232 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
233 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
234 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200235 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500236 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900237 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500238 {
239 .pixel_clock = 36000000,
240 .conf = {
241 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
242 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
243 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
244 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
245 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900246 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500247 {
248 .pixel_clock = 40000000,
249 .conf = {
250 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
251 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
252 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
253 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
254 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900255 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500256 {
257 .pixel_clock = 65000000,
258 .conf = {
259 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
260 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
261 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
262 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
263 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900264 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500265 {
Shirish Se1d883c2014-03-13 14:28:27 +0900266 .pixel_clock = 71000000,
267 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530268 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
269 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
270 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900271 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
272 },
273 },
274 {
275 .pixel_clock = 73250000,
276 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530277 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
278 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
279 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900280 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
281 },
282 },
283 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500284 .pixel_clock = 74176000,
285 .conf = {
286 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
287 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
288 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
289 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
290 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900291 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500292 {
293 .pixel_clock = 74250000,
294 .conf = {
295 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
296 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
297 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200298 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500299 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900300 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500301 {
302 .pixel_clock = 83500000,
303 .conf = {
304 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
305 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
306 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
307 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
308 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900309 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500310 {
311 .pixel_clock = 106500000,
312 .conf = {
313 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
314 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
315 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
316 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
317 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900318 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500319 {
320 .pixel_clock = 108000000,
321 .conf = {
322 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
323 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
324 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
325 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
326 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900327 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500328 {
Shirish Se1d883c2014-03-13 14:28:27 +0900329 .pixel_clock = 115500000,
330 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530331 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
332 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
333 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900334 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
335 },
336 },
337 {
338 .pixel_clock = 119000000,
339 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530340 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
341 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
342 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900343 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
344 },
345 },
346 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500347 .pixel_clock = 146250000,
348 .conf = {
349 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
350 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
351 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
352 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
353 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900354 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500355 {
356 .pixel_clock = 148500000,
357 .conf = {
358 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
359 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
360 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200361 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500362 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900363 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900364};
365
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530366static const struct hdmiphy_config hdmiphy_5420_configs[] = {
367 {
368 .pixel_clock = 25200000,
369 .conf = {
370 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
371 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
372 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
373 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
374 },
375 },
376 {
377 .pixel_clock = 27000000,
378 .conf = {
379 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
380 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
381 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
382 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
383 },
384 },
385 {
386 .pixel_clock = 27027000,
387 .conf = {
388 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
389 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
390 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
391 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
392 },
393 },
394 {
395 .pixel_clock = 36000000,
396 .conf = {
397 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
398 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
399 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
400 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
401 },
402 },
403 {
404 .pixel_clock = 40000000,
405 .conf = {
406 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
407 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
408 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
409 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
410 },
411 },
412 {
413 .pixel_clock = 65000000,
414 .conf = {
415 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
416 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
417 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
418 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
419 },
420 },
421 {
422 .pixel_clock = 71000000,
423 .conf = {
424 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
425 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
426 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
427 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
428 },
429 },
430 {
431 .pixel_clock = 73250000,
432 .conf = {
433 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
434 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
435 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
436 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
437 },
438 },
439 {
440 .pixel_clock = 74176000,
441 .conf = {
442 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
443 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
444 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
445 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
446 },
447 },
448 {
449 .pixel_clock = 74250000,
450 .conf = {
451 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
452 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
453 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
454 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
455 },
456 },
457 {
458 .pixel_clock = 83500000,
459 .conf = {
460 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
461 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
462 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
463 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
464 },
465 },
466 {
467 .pixel_clock = 88750000,
468 .conf = {
469 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
470 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
471 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
472 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
473 },
474 },
475 {
476 .pixel_clock = 106500000,
477 .conf = {
478 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
479 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
480 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
481 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
482 },
483 },
484 {
485 .pixel_clock = 108000000,
486 .conf = {
487 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
488 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
489 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
490 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
491 },
492 },
493 {
494 .pixel_clock = 115500000,
495 .conf = {
496 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
497 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
498 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
499 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
500 },
501 },
502 {
503 .pixel_clock = 146250000,
504 .conf = {
505 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
506 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
507 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
508 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
509 },
510 },
511 {
512 .pixel_clock = 148500000,
513 .conf = {
514 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
515 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
516 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
517 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
518 },
519 },
520};
521
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900522static const struct hdmiphy_config hdmiphy_5433_configs[] = {
523 {
524 .pixel_clock = 27000000,
525 .conf = {
526 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46,
527 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
528 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
529 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
530 },
531 },
532 {
533 .pixel_clock = 27027000,
534 .conf = {
535 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
536 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5,
537 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
538 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
539 },
540 },
541 {
542 .pixel_clock = 40000000,
543 .conf = {
544 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
545 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
546 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
547 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
548 },
549 },
550 {
551 .pixel_clock = 50000000,
552 .conf = {
553 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
554 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
555 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
556 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
557 },
558 },
559 {
560 .pixel_clock = 65000000,
561 .conf = {
562 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
563 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
564 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
565 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
566 },
567 },
568 {
569 .pixel_clock = 74176000,
570 .conf = {
571 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
572 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
573 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
574 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
575 },
576 },
577 {
578 .pixel_clock = 74250000,
579 .conf = {
580 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
581 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
582 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
583 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
584 },
585 },
586 {
587 .pixel_clock = 108000000,
588 .conf = {
589 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
590 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
591 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
592 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
593 },
594 },
595 {
596 .pixel_clock = 148500000,
597 .conf = {
598 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
599 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
600 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
601 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
602 },
603 },
604};
605
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100606static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900607 "hdmi", "sclk_hdmi"
608};
609
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100610static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900611 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
612};
613
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900614static const char * const hdmi_clk_gates5433[] = {
615 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
616};
617
618static const char * const hdmi_clk_muxes5433[] = {
619 "oscclk", "tmds_clko", "tmds_clko_user",
620 "oscclk", "pixel_clko", "pixel_clko_user"
621};
622
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900623static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
624 .type = HDMI_TYPE13,
625 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900626 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
627 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530628};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900629
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100630static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900631 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100632 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900633 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
634 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900635};
636
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900637static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
638 .type = HDMI_TYPE14,
639 .is_apb_phy = 1,
640 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900641 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
642 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200643};
644
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900645static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
646 .type = HDMI_TYPE14,
647 .is_apb_phy = 1,
648 .has_sysreg = 1,
649 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
650 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
651 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
652};
653
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200654static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
655{
656 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
657 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
658 return reg_id;
659}
660
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900661static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
662{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200663 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900664}
665
666static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
667 u32 reg_id, u8 value)
668{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200669 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900670}
671
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200672static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
673 int bytes, u32 val)
674{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200675 reg_id = hdmi_map_reg(hdata, reg_id);
676
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200677 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200678 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200679 val >>= 8;
680 reg_id += 4;
681 }
682}
683
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900684static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
685 u32 reg_id, u32 value, u32 mask)
686{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200687 u32 old;
688
689 reg_id = hdmi_map_reg(hdata, reg_id);
690 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900691 value = (value & mask) | (old & ~mask);
692 writel(value, hdata->regs + reg_id);
693}
694
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900695static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
696 u32 reg_offset, const u8 *buf, u32 len)
697{
698 if ((reg_offset + len) > 32)
699 return -EINVAL;
700
701 if (hdata->hdmiphy_port) {
702 int ret;
703
704 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
705 if (ret == len)
706 return 0;
707 return ret;
708 } else {
709 int i;
710 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200711 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900712 ((reg_offset + i)<<2));
713 return 0;
714 }
715}
716
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900717static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
718{
719 int i, ret;
720
721 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
722 ret = clk_prepare_enable(hdata->clk_gates[i]);
723 if (!ret)
724 continue;
725
726 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
727 hdata->drv_data->clk_gates.data[i], ret);
728 while (i--)
729 clk_disable_unprepare(hdata->clk_gates[i]);
730 return ret;
731 }
732
733 return 0;
734}
735
736static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
737{
738 int i = hdata->drv_data->clk_gates.count;
739
740 while (i--)
741 clk_disable_unprepare(hdata->clk_gates[i]);
742}
743
744static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
745{
746 struct device *dev = hdata->dev;
747 int ret = 0;
748 int i;
749
750 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
751 struct clk **c = &hdata->clk_muxes[i];
752
753 ret = clk_set_parent(c[2], c[to_phy]);
754 if (!ret)
755 continue;
756
757 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
758 hdata->drv_data->clk_muxes.data[i + 2],
759 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
760 }
761
762 return ret;
763}
764
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530765static u8 hdmi_chksum(struct hdmi_context *hdata,
766 u32 start, u8 len, u32 hdr_sum)
767{
768 int i;
769
770 /* hdr_sum : header0 + header1 + header2
771 * start : start address of packet byte1
772 * len : packet bytes - 1 */
773 for (i = 0; i < len; ++i)
774 hdr_sum += 0xff & hdmi_reg_read(hdata, start + i * 4);
775
776 /* return 2's complement of 8 bit hdr_sum */
777 return (u8)(~(hdr_sum & 0xff) + 1);
778}
779
780static void hdmi_reg_infoframe(struct hdmi_context *hdata,
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530781 union hdmi_infoframe *infoframe)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530782{
783 u32 hdr_sum;
784 u8 chksum;
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200785 u8 ar;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530786
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530787 if (hdata->dvi_mode) {
788 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
789 HDMI_VSI_CON_DO_NOT_TRANSMIT);
790 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
791 HDMI_AVI_CON_DO_NOT_TRANSMIT);
792 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
793 return;
794 }
795
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530796 switch (infoframe->any.type) {
797 case HDMI_INFOFRAME_TYPE_AVI:
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530798 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530799 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER0, infoframe->any.type);
800 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER1,
801 infoframe->any.version);
802 hdmi_reg_writeb(hdata, HDMI_AVI_HEADER2, infoframe->any.length);
803 hdr_sum = infoframe->any.type + infoframe->any.version +
804 infoframe->any.length;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530805
806 /* Output format zero hardcoded ,RGB YBCR selection */
807 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 0 << 5 |
808 AVI_ACTIVE_FORMAT_VALID |
809 AVI_UNDERSCANNED_DISPLAY_VALID);
810
Shirish S46154152014-03-13 10:58:28 +0530811 /*
812 * Set the aspect ratio as per the mode, mentioned in
813 * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard
814 */
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200815 ar = hdata->current_mode.picture_aspect_ratio;
816 switch (ar) {
Shirish S46154152014-03-13 10:58:28 +0530817 case HDMI_PICTURE_ASPECT_4_3:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200818 ar |= AVI_4_3_CENTER_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530819 break;
820 case HDMI_PICTURE_ASPECT_16_9:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200821 ar |= AVI_16_9_CENTER_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530822 break;
823 case HDMI_PICTURE_ASPECT_NONE:
824 default:
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200825 ar |= AVI_SAME_AS_PIC_ASPECT_RATIO;
Shirish S46154152014-03-13 10:58:28 +0530826 break;
827 }
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200828 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), ar);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530829
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +0200830 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), hdata->cea_video_id);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530831
832 chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1),
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530833 infoframe->any.length, hdr_sum);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530834 DRM_DEBUG_KMS("AVI checksum = 0x%x\n", chksum);
835 hdmi_reg_writeb(hdata, HDMI_AVI_CHECK_SUM, chksum);
836 break;
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530837 case HDMI_INFOFRAME_TYPE_AUDIO:
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530838 hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530839 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER0, infoframe->any.type);
840 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER1,
841 infoframe->any.version);
842 hdmi_reg_writeb(hdata, HDMI_AUI_HEADER2, infoframe->any.length);
843 hdr_sum = infoframe->any.type + infoframe->any.version +
844 infoframe->any.length;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530845 chksum = hdmi_chksum(hdata, HDMI_AUI_BYTE(1),
Sachin Kamatd34d59b2014-02-04 08:40:18 +0530846 infoframe->any.length, hdr_sum);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530847 DRM_DEBUG_KMS("AUI checksum = 0x%x\n", chksum);
848 hdmi_reg_writeb(hdata, HDMI_AUI_CHECK_SUM, chksum);
849 break;
850 default:
851 break;
852 }
853}
854
Sean Pauld9716ee2014-01-30 16:19:29 -0500855static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
856 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500857{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200858 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500859
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200860 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200861 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530862
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200863 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500864}
865
Sean Pauld9716ee2014-01-30 16:19:29 -0500866static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900867{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200868 drm_connector_unregister(connector);
869 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900870}
871
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100872static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300873 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500874 .fill_modes = drm_helper_probe_single_connector_modes,
875 .detect = hdmi_detect,
876 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300877 .reset = drm_atomic_helper_connector_reset,
878 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
879 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500880};
881
882static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900883{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200884 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500885 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200886 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900887
Inki Dae8fa04aa2014-03-13 16:38:31 +0900888 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500889 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900890
Inki Dae8fa04aa2014-03-13 16:38:31 +0900891 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500892 if (!edid)
893 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900894
Sean Pauld9716ee2014-01-30 16:19:29 -0500895 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500896 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
897 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500898 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500899
Sean Pauld9716ee2014-01-30 16:19:29 -0500900 drm_mode_connector_update_edid_property(connector, edid);
901
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200902 ret = drm_add_edid_modes(connector, edid);
903
904 kfree(edid);
905
906 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900907}
908
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900909static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900910{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100911 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900912 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900913
Andrzej Hajda65e98032015-11-02 14:16:41 +0100914 for (i = 0; i < confs->count; i++)
915 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500916 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500917
918 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
919 return -EINVAL;
920}
921
Sean Pauld9716ee2014-01-30 16:19:29 -0500922static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500923 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900924{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200925 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900926 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900927
Rahul Sharma16844fb2013-06-10 14:50:00 +0530928 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
929 mode->hdisplay, mode->vdisplay, mode->vrefresh,
930 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
931 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900932
Rahul Sharma16844fb2013-06-10 14:50:00 +0530933 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900934 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500935 return MODE_BAD;
936
937 return MODE_OK;
938}
939
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100940static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500941 .get_modes = hdmi_get_modes,
942 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500943};
944
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300945static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500946{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300947 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500948 struct drm_connector *connector = &hdata->connector;
949 int ret;
950
Sean Pauld9716ee2014-01-30 16:19:29 -0500951 connector->interlace_allowed = true;
952 connector->polled = DRM_CONNECTOR_POLL_HPD;
953
954 ret = drm_connector_init(hdata->drm_dev, connector,
955 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
956 if (ret) {
957 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900958 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500959 }
960
961 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100962 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300963 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500964
965 return 0;
966}
967
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300968static bool hdmi_mode_fixup(struct drm_encoder *encoder,
969 const struct drm_display_mode *mode,
970 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500971{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300972 struct drm_device *dev = encoder->dev;
973 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500974 struct drm_display_mode *m;
975 int mode_ok;
976
Sean Paulf041b252014-01-30 16:19:15 -0500977 drm_mode_set_crtcinfo(adjusted_mode, 0);
978
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300979 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
980 if (connector->encoder == encoder)
981 break;
982 }
983
984 if (connector->encoder != encoder)
985 return true;
986
Sean Pauld9716ee2014-01-30 16:19:29 -0500987 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500988
Sean Pauld9716ee2014-01-30 16:19:29 -0500989 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300990 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500991
992 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900993 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500994 */
995 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500996 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500997
Sean Pauld9716ee2014-01-30 16:19:29 -0500998 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500999 DRM_INFO("desired mode doesn't exist so\n");
1000 DRM_INFO("use the most suitable mode among modes.\n");
1001
1002 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
1003 m->hdisplay, m->vdisplay, m->vrefresh);
1004
Sean Paul75626852014-01-30 16:19:16 -05001005 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -05001006 break;
1007 }
1008 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001009
1010 return true;
Sean Paulf041b252014-01-30 16:19:15 -05001011}
1012
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001013static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001014{
1015 u32 n, cts;
1016
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001017 cts = (freq % 9) ? 27000 : 30000;
1018 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001019
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001020 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
1021 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
1022 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001023 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001024}
1025
1026static void hdmi_audio_init(struct hdmi_context *hdata)
1027{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +05301028 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001029 u32 data_num, bit_ch, sample_frq;
1030 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001031
1032 sample_rate = 44100;
1033 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001034
1035 switch (bits_per_sample) {
1036 case 20:
1037 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001038 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001039 break;
1040 case 24:
1041 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001042 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001043 break;
1044 default:
1045 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001046 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001047 break;
1048 }
1049
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001050 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001051
1052 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1053 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1054 | HDMI_I2S_MUX_ENABLE);
1055
1056 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1057 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1058
1059 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1060
1061 sample_frq = (sample_rate == 44100) ? 0 :
1062 (sample_rate == 48000) ? 2 :
1063 (sample_rate == 32000) ? 3 :
1064 (sample_rate == 96000) ? 0xa : 0x0;
1065
1066 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1067 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1068
1069 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1070 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1071
1072 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1073 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1074 | HDMI_I2S_SEL_LRCK(6));
1075 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1076 | HDMI_I2S_SEL_SDATA2(4));
1077 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1078 | HDMI_I2S_SEL_SDATA2(2));
1079 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1080
1081 /* I2S_CON_1 & 2 */
1082 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1083 | HDMI_I2S_L_CH_LOW_POL);
1084 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1085 | HDMI_I2S_SET_BIT_CH(bit_ch)
1086 | HDMI_I2S_SET_SDATA_BIT(data_num)
1087 | HDMI_I2S_BASIC_FORMAT);
1088
1089 /* Configure register related to CUV information */
1090 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1091 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1092 | HDMI_I2S_COPYRIGHT
1093 | HDMI_I2S_LINEAR_PCM
1094 | HDMI_I2S_CONSUMER_FORMAT);
1095 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1096 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1097 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1098 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1099 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1100 HDMI_I2S_ORG_SMP_FREQ_44_1
1101 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1102 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1103
1104 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1105}
1106
1107static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1108{
Seung-Woo Kim872d20d2012-04-24 17:39:15 +09001109 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001110 return;
1111
1112 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1113 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1114 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1115}
1116
Rahul Sharmabfa48422014-04-03 20:41:04 +05301117static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001118{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301119 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001120
Rahul Sharmabfa48422014-04-03 20:41:04 +05301121 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1122 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001123
Rahul Sharmabfa48422014-04-03 20:41:04 +05301124 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1125 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001126}
1127
1128static void hdmi_conf_init(struct hdmi_context *hdata)
1129{
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301130 union hdmi_infoframe infoframe;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301131
Sean Paul77006a72013-01-16 10:17:20 -05001132 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001133 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1134 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001135
1136 /* choose HDMI mode */
1137 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1138 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001139 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301140 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001141 /* disable bluescreen */
1142 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001143
Seung-Woo Kim872d20d2012-04-24 17:39:15 +09001144 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d2012-04-24 17:39:15 +09001145 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1146 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1147 hdmi_reg_writeb(hdata, HDMI_CON_2,
1148 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1149 }
1150
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001151 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001152 /* choose bluescreen (fecal) color */
1153 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1154 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1155 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1156
1157 /* enable AVI packet every vsync, fixes purple line problem */
1158 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1159 /* force RGB, look to CEA-861-D, table 7 for more detail */
1160 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1161 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1162
1163 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1164 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1165 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1166 } else {
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301167 infoframe.any.type = HDMI_INFOFRAME_TYPE_AVI;
1168 infoframe.any.version = HDMI_AVI_VERSION;
1169 infoframe.any.length = HDMI_AVI_LENGTH;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301170 hdmi_reg_infoframe(hdata, &infoframe);
1171
Sachin Kamatd34d59b2014-02-04 08:40:18 +05301172 infoframe.any.type = HDMI_INFOFRAME_TYPE_AUDIO;
1173 infoframe.any.version = HDMI_AUI_VERSION;
1174 infoframe.any.length = HDMI_AUI_LENGTH;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301175 hdmi_reg_infoframe(hdata, &infoframe);
1176
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001177 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001178 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1179 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001180}
1181
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001182static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1183{
1184 int tries;
1185
1186 for (tries = 0; tries < 10; ++tries) {
1187 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1188
1189 if (val & HDMI_PHY_STATUS_READY) {
1190 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1191 return;
1192 }
1193 usleep_range(10, 20);
1194 }
1195
1196 DRM_ERROR("PLL could not reach steady state\n");
1197}
1198
Rahul Sharma16844fb2013-06-10 14:50:00 +05301199static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001200{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001201 struct drm_display_mode *m = &hdata->current_mode;
1202 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001203
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001204 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1205 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1206 (m->htotal << 12) | m->vtotal);
1207
1208 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1209 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1210
1211 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1212 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1213
1214 val = (m->hsync_start - m->hdisplay - 2);
1215 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001216 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001217 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1218
1219 /*
1220 * Quirk requirement for exynos HDMI IP design,
1221 * 2 pixels less than the actual calculation for hsync_start
1222 * and end.
1223 */
1224
1225 /* Following values & calculations differ for different type of modes */
1226 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001227 val = ((m->vsync_end - m->vdisplay) / 2);
1228 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1229 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1230
1231 val = m->vtotal / 2;
1232 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1233 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1234
1235 val = (m->vtotal +
1236 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1237 val |= m->vtotal << 11;
1238 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1239
1240 val = ((m->vtotal / 2) + 7);
1241 val |= ((m->vtotal / 2) + 2) << 12;
1242 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1243
1244 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1245 val |= ((m->htotal / 2) +
1246 (m->hsync_start - m->hdisplay)) << 12;
1247 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1248
1249 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1250 (m->vtotal - m->vdisplay) / 2);
1251 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1252
1253 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1254 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001255 val = m->vtotal;
1256 val |= (m->vtotal - m->vdisplay) << 11;
1257 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1258
1259 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1260
1261 val = (m->vsync_end - m->vdisplay);
1262 val |= ((m->vsync_start - m->vdisplay) << 12);
1263 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1264
1265 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1266 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1267 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1268 m->vtotal - m->vdisplay);
1269 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001270 }
1271
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001272 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1273 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1274 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1275 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001276}
1277
Rahul Sharma16844fb2013-06-10 14:50:00 +05301278static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001279{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001280 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001281
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001282 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1283 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1284 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1285 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001286 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001287 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1288 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1289 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1290 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1291
1292 /*
1293 * Quirk requirement for exynos 5 HDMI IP design,
1294 * 2 pixels less than the actual calculation for hsync_start
1295 * and end.
1296 */
1297
1298 /* Following values & calculations differ for different type of modes */
1299 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001300 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1301 (m->vsync_end - m->vdisplay) / 2);
1302 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1303 (m->vsync_start - m->vdisplay) / 2);
1304 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1305 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1306 (m->vtotal - m->vdisplay) / 2);
1307 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1308 m->vtotal - m->vdisplay / 2);
1309 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1310 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1311 (m->vtotal / 2) + 7);
1312 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1313 (m->vtotal / 2) + 2);
1314 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1315 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1316 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1317 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1318 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1319 (m->vtotal - m->vdisplay) / 2);
1320 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1321 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1322 m->vtotal - m->vdisplay / 2);
1323 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1324 (m->vtotal / 2) + 1);
1325 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1326 (m->vtotal / 2) + 1);
1327 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1328 (m->vtotal / 2) + 1);
1329 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1330 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1331 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001332 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1333 m->vsync_end - m->vdisplay);
1334 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1335 m->vsync_start - m->vdisplay);
1336 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1337 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1338 m->vtotal - m->vdisplay);
1339 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1340 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1341 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1342 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1343 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1344 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1345 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1346 m->vtotal - m->vdisplay);
1347 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001348 }
1349
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001350 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1351 m->hsync_start - m->hdisplay - 2);
1352 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1353 m->hsync_end - m->hdisplay - 2);
1354 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1355 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1356 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1357 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1358 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1359 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1360 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1361 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1362 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1363 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1364 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1365 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1366 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1367 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1368 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1369 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1370 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1371 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001372
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001373 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1374 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1375 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1376 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001377 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1378 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001379}
1380
Rahul Sharma16844fb2013-06-10 14:50:00 +05301381static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001382{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001383 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301384 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001385 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301386 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001387
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001388 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001389}
1390
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001391static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1392{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001393 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1394 usleep_range(10000, 12000);
1395 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1396 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001397 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001398 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001399 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001400 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001401}
1402
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001403static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1404{
1405 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1406
1407 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1408 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1409}
1410
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001411static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1412{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001413 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001414 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001415
Andrzej Hajda4677f512016-03-23 14:15:12 +01001416 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1417 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001418 DRM_ERROR("failed to find hdmiphy conf\n");
1419 return;
1420 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001421 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1422
1423 hdmi_clk_set_parents(hdata, false);
1424
1425 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001426
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001427 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001428 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001429 if (ret) {
1430 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001431 return;
1432 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001433 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001434 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001435 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001436 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001437}
1438
1439static void hdmi_conf_apply(struct hdmi_context *hdata)
1440{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301441 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001442 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001443 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301444 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001445 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001446}
1447
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001448static void hdmi_mode_set(struct drm_encoder *encoder,
1449 struct drm_display_mode *mode,
1450 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001451{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001452 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001453 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001454
YoungJun Chocbc4c332013-06-12 10:44:40 +09001455 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1456 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001457 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001458 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001459
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001460 drm_mode_copy(&hdata->current_mode, m);
Andrzej Hajdac93aaeb2015-07-09 16:28:10 +02001461 hdata->cea_video_id = drm_match_cea_mode(mode);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001462}
1463
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001464static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1465{
1466 if (!hdata->sysreg)
1467 return;
1468
1469 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1470 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1471}
1472
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001473static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001474{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001475 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001476 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001477
Sean Paulaf65c802014-01-30 16:19:27 -05001478 pm_runtime_get_sync(hdata->dev);
1479
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001480 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001481 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1482
Rahul Sharma049d34e2014-05-20 10:36:05 +05301483 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1484 PMU_HDMI_PHY_ENABLE_BIT, 1);
1485
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001486 hdmi_set_refclk(hdata, true);
1487
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001488 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1489
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001490 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001491
1492 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001493}
1494
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001495static void hdmiphy_disable(struct hdmi_context *hdata)
1496{
1497 if (!hdata->powered)
1498 return;
1499
1500 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1501
1502 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1503
1504 hdmi_set_refclk(hdata, false);
1505
1506 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1507 PMU_HDMI_PHY_ENABLE_BIT, 0);
1508
1509 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1510
1511 pm_runtime_put_sync(hdata->dev);
1512
1513 hdata->powered = false;
1514}
1515
1516static void hdmi_enable(struct drm_encoder *encoder)
1517{
1518 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1519
1520 hdmiphy_enable(hdata);
1521 hdmi_conf_apply(hdata);
1522}
1523
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001524static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001525{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001526 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001527 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001528 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001529
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001530 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001531 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001532
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001533 /*
1534 * The SFRs of VP and Mixer are updated by Vertical Sync of
1535 * Timing generator which is a part of HDMI so the sequence
1536 * to disable TV Subsystem should be as following,
1537 * VP -> Mixer -> HDMI
1538 *
1539 * Below codes will try to disable Mixer and VP(if used)
1540 * prior to disabling HDMI.
1541 */
1542 if (crtc)
1543 funcs = crtc->helper_private;
1544 if (funcs && funcs->disable)
1545 (*funcs->disable)(crtc);
1546
Sean Paul724fd142014-05-09 15:05:10 +09001547 cancel_delayed_work(&hdata->hotplug_work);
1548
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001549 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001550}
1551
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001552static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001553 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001554 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001555 .enable = hdmi_enable,
1556 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001557};
1558
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001559static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001560 .destroy = drm_encoder_cleanup,
1561};
1562
Sean Paul724fd142014-05-09 15:05:10 +09001563static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001564{
Sean Paul724fd142014-05-09 15:05:10 +09001565 struct hdmi_context *hdata;
1566
1567 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001568
Sean Paul45517892014-01-30 16:19:05 -05001569 if (hdata->drm_dev)
1570 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001571}
1572
1573static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1574{
1575 struct hdmi_context *hdata = arg;
1576
1577 mod_delayed_work(system_wq, &hdata->hotplug_work,
1578 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001579
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001580 return IRQ_HANDLED;
1581}
1582
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001583static int hdmi_clks_get(struct hdmi_context *hdata,
1584 const struct string_array_spec *names,
1585 struct clk **clks)
1586{
1587 struct device *dev = hdata->dev;
1588 int i;
1589
1590 for (i = 0; i < names->count; ++i) {
1591 struct clk *clk = devm_clk_get(dev, names->data[i]);
1592
1593 if (IS_ERR(clk)) {
1594 int ret = PTR_ERR(clk);
1595
1596 dev_err(dev, "Cannot get clock %s, %d\n",
1597 names->data[i], ret);
1598
1599 return ret;
1600 }
1601
1602 clks[i] = clk;
1603 }
1604
1605 return 0;
1606}
1607
1608static int hdmi_clk_init(struct hdmi_context *hdata)
1609{
1610 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1611 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1612 struct device *dev = hdata->dev;
1613 struct clk **clks;
1614 int ret;
1615
1616 if (!count)
1617 return 0;
1618
1619 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1620 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001621 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001622
1623 hdata->clk_gates = clks;
1624 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1625
1626 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1627 if (ret)
1628 return ret;
1629
1630 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1631}
1632
1633
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001634static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1635{
1636 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1637 phy_clk);
1638
1639 if (enable)
1640 hdmiphy_enable(hdata);
1641 else
1642 hdmiphy_disable(hdata);
1643}
1644
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001645static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001646{
1647 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001648 int i, ret;
1649
1650 DRM_DEBUG_KMS("HDMI resource init\n");
1651
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001652 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1653 if (IS_ERR(hdata->hpd_gpio)) {
1654 DRM_ERROR("cannot get hpd gpio property\n");
1655 return PTR_ERR(hdata->hpd_gpio);
1656 }
1657
1658 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1659 if (hdata->irq < 0) {
1660 DRM_ERROR("failed to get GPIO irq\n");
1661 return hdata->irq;
1662 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001663
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001664 ret = hdmi_clk_init(hdata);
1665 if (ret)
1666 return ret;
1667
1668 ret = hdmi_clk_set_parents(hdata, false);
1669 if (ret)
1670 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001671
Milo Kimc0d656d2016-08-31 15:14:27 +09001672 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001673 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001674
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001675 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001676 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001677 if (ret != -EPROBE_DEFER)
1678 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001679 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001680 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001681
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001682 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001683
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001684 if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001685 return 0;
1686
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001687 if (IS_ERR(hdata->reg_hdmi_en))
1688 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001689
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001690 ret = regulator_enable(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001691 if (ret)
1692 DRM_ERROR("failed to enable hdmi-en regulator\n");
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001693
Inki Daedf5225b2014-05-29 18:28:02 +09001694 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001695}
1696
Rahul Sharma22c4f422012-10-04 20:48:55 +05301697static struct of_device_id hdmi_match_types[] = {
1698 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001699 .compatible = "samsung,exynos4210-hdmi",
1700 .data = &exynos4210_hdmi_driver_data,
1701 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301702 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001703 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301704 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301705 .compatible = "samsung,exynos5420-hdmi",
1706 .data = &exynos5420_hdmi_driver_data,
1707 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001708 .compatible = "samsung,exynos5433-hdmi",
1709 .data = &exynos5433_hdmi_driver_data,
1710 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301711 /* end node */
1712 }
1713};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001714MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301715
Inki Daef37cd5e2014-05-09 14:25:20 +09001716static int hdmi_bind(struct device *dev, struct device *master, void *data)
1717{
1718 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001719 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001720 struct drm_encoder *encoder = &hdata->encoder;
1721 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001722
Inki Daef37cd5e2014-05-09 14:25:20 +09001723 hdata->drm_dev = drm_dev;
1724
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001725 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1726 EXYNOS_DISPLAY_TYPE_HDMI);
1727 if (pipe < 0)
1728 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001729
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001730 hdata->phy_clk.enable = hdmiphy_clk_enable;
1731
1732 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1733
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001734 encoder->possible_crtcs = 1 << pipe;
1735
1736 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1737
1738 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001739 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001740
1741 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1742
1743 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001744 if (ret) {
1745 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001746 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001747 return ret;
1748 }
1749
1750 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001751}
1752
1753static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1754{
Inki Daef37cd5e2014-05-09 14:25:20 +09001755}
1756
1757static const struct component_ops hdmi_component_ops = {
1758 .bind = hdmi_bind,
1759 .unbind = hdmi_unbind,
1760};
1761
Milo Kim1caa3602016-08-31 15:14:25 +09001762static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001763{
1764 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1765 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001766 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001767
1768 np = of_find_compatible_node(NULL, NULL, compatible_str);
1769 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001770 np = of_get_next_parent(np);
1771 else
1772 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001773
Milo Kim1caa3602016-08-31 15:14:25 +09001774 if (!np) {
1775 DRM_ERROR("Failed to find ddc node in device tree\n");
1776 return -ENODEV;
1777 }
1778
1779 adpt = of_find_i2c_adapter_by_node(np);
1780 of_node_put(np);
1781
1782 if (!adpt) {
1783 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1784 return -EPROBE_DEFER;
1785 }
1786
1787 hdata->ddc_adpt = adpt;
1788
1789 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001790}
1791
Milo Kimb5413022016-08-31 15:14:26 +09001792static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001793{
1794 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001795 struct device_node *np;
1796 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001797
Milo Kimb5413022016-08-31 15:14:26 +09001798 np = of_find_compatible_node(NULL, NULL, compatible_str);
1799 if (!np) {
1800 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1801 if (!np) {
1802 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1803 return -ENODEV;
1804 }
1805 }
1806
1807 if (hdata->drv_data->is_apb_phy) {
1808 hdata->regs_hdmiphy = of_iomap(np, 0);
1809 if (!hdata->regs_hdmiphy) {
1810 DRM_ERROR("failed to ioremap hdmi phy\n");
1811 ret = -ENOMEM;
1812 goto out;
1813 }
1814 } else {
1815 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1816 if (!hdata->hdmiphy_port) {
1817 DRM_INFO("Failed to get hdmi phy i2c client\n");
1818 ret = -EPROBE_DEFER;
1819 goto out;
1820 }
1821 }
1822
1823out:
1824 of_node_put(np);
1825 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001826}
1827
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001828static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001829{
1830 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001831 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001832 struct resource *res;
1833 int ret;
1834
Andrzej Hajda930865f2014-11-17 09:54:20 +01001835 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1836 if (!hdata)
1837 return -ENOMEM;
1838
Marek Szyprowski57a64122016-04-01 15:17:44 +02001839 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001840
Andrzej Hajda930865f2014-11-17 09:54:20 +01001841 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001842
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001843 hdata->dev = dev;
1844
1845 ret = hdmi_resources_init(hdata);
1846 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001847 if (ret != -EPROBE_DEFER)
1848 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001849 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001850 }
1851
1852 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001853 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001854 if (IS_ERR(hdata->regs)) {
1855 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001856 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001857 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001858
Milo Kim1caa3602016-08-31 15:14:25 +09001859 ret = hdmi_get_ddc_adapter(hdata);
1860 if (ret)
1861 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001862
Milo Kimb5413022016-08-31 15:14:26 +09001863 ret = hdmi_get_phy_io(hdata);
1864 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001865 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001866
Sean Paul724fd142014-05-09 15:05:10 +09001867 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1868
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001869 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001870 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001871 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001872 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001873 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001874 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001875 goto err_hdmiphy;
1876 }
1877
Rahul Sharma049d34e2014-05-20 10:36:05 +05301878 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1879 "samsung,syscon-phandle");
1880 if (IS_ERR(hdata->pmureg)) {
1881 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001882 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301883 goto err_hdmiphy;
1884 }
1885
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001886 if (hdata->drv_data->has_sysreg) {
1887 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1888 "samsung,sysreg-phandle");
1889 if (IS_ERR(hdata->sysreg)) {
1890 DRM_ERROR("sysreg regmap lookup failed.\n");
1891 ret = -EPROBE_DEFER;
1892 goto err_hdmiphy;
1893 }
1894 }
1895
Sean Paulaf65c802014-01-30 16:19:27 -05001896 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001897
Inki Daedf5225b2014-05-29 18:28:02 +09001898 ret = component_add(&pdev->dev, &hdmi_component_ops);
1899 if (ret)
1900 goto err_disable_pm_runtime;
1901
1902 return ret;
1903
1904err_disable_pm_runtime:
1905 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001906
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001907err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001908 if (hdata->hdmiphy_port)
1909 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301910 if (hdata->regs_hdmiphy)
1911 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001912err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001913 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001914
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001915 return ret;
1916}
1917
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001918static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001919{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001920 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001921
Sean Paul724fd142014-05-09 15:05:10 +09001922 cancel_delayed_work_sync(&hdata->hotplug_work);
1923
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001924 component_del(&pdev->dev, &hdmi_component_ops);
1925
1926 pm_runtime_disable(&pdev->dev);
1927
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001928 if (!IS_ERR(hdata->reg_hdmi_en))
1929 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001930
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001931 if (hdata->hdmiphy_port)
1932 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001933
Arvind Yadavd7420002016-10-19 15:34:16 +05301934 if (hdata->regs_hdmiphy)
1935 iounmap(hdata->regs_hdmiphy);
1936
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001937 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001938
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001939 return 0;
1940}
1941
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001942#ifdef CONFIG_PM
1943static int exynos_hdmi_suspend(struct device *dev)
1944{
1945 struct hdmi_context *hdata = dev_get_drvdata(dev);
1946
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001947 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001948
1949 return 0;
1950}
1951
1952static int exynos_hdmi_resume(struct device *dev)
1953{
1954 struct hdmi_context *hdata = dev_get_drvdata(dev);
1955 int ret;
1956
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001957 ret = hdmi_clk_enable_gates(hdata);
1958 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001959 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001960
1961 return 0;
1962}
1963#endif
1964
1965static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1966 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1967};
1968
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001969struct platform_driver hdmi_driver = {
1970 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001971 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001972 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301973 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001974 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001975 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301976 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001977 },
1978};