blob: 5ed8b1effe71203d23808072d03534f578fdfb78 [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 Sharma5a325072012-10-04 20:48:54 +053050enum hdmi_type {
51 HDMI_TYPE13,
52 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020053 HDMI_TYPE_COUNT
54};
55
56#define HDMI_MAPPED_BASE 0xffff0000
57
58enum hdmi_mapped_regs {
59 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
60 HDMI_PHY_RSTOUT,
61 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020062 HDMI_ACR_MCTS0,
63 HDMI_ACR_CTS0,
64 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020065};
66
67static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
68 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
69 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
70 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020071 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
72 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
73 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053074};
75
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020076static const char * const supply[] = {
77 "vdd",
78 "vdd_osc",
79 "vdd_pll",
80};
81
Andrzej Hajda65e98032015-11-02 14:16:41 +010082struct hdmiphy_config {
83 int pixel_clock;
84 u8 conf[32];
85};
86
87struct hdmiphy_configs {
88 int count;
89 const struct hdmiphy_config *data;
90};
91
Andrzej Hajda9be7e982016-01-14 14:22:47 +090092struct string_array_spec {
93 int count;
94 const char * const *data;
95};
96
97#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
98
Inki Daebfe4e842014-03-06 14:18:17 +090099struct hdmi_driver_data {
100 unsigned int type;
101 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900102 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100103 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900104 struct string_array_spec clk_gates;
105 /*
106 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
107 * required parents of clock when HDMI-PHY is respectively off or on.
108 */
109 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900110};
111
Joonyoung Shim590f4182012-03-16 18:47:14 +0900112struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300113 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900114 struct device *dev;
115 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500116 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900117 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900118 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900119 struct delayed_work hotplug_work;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530120 struct drm_display_mode current_mode;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200121 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900122
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200123 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900124 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200125 struct i2c_client *hdmiphy_port;
126 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900127 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200128 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530129 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900130 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900131 struct clk **clk_gates;
132 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200133 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
134 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900135 struct exynos_drm_clk phy_clk;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900136};
137
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300138static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100139{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900140 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100141}
142
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200143static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
144{
145 return container_of(c, struct hdmi_context, connector);
146}
147
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900148static const struct hdmiphy_config hdmiphy_v13_configs[] = {
149 {
150 .pixel_clock = 27000000,
151 .conf = {
152 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
153 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
154 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200155 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900156 },
157 },
158 {
159 .pixel_clock = 27027000,
160 .conf = {
161 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
162 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
163 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200164 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900165 },
166 },
167 {
168 .pixel_clock = 74176000,
169 .conf = {
170 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
171 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
172 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200173 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900174 },
175 },
176 {
177 .pixel_clock = 74250000,
178 .conf = {
179 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
180 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
181 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200182 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900183 },
184 },
185 {
186 .pixel_clock = 148500000,
187 .conf = {
188 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
189 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
190 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200191 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900192 },
193 },
194};
195
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500196static const struct hdmiphy_config hdmiphy_v14_configs[] = {
197 {
198 .pixel_clock = 25200000,
199 .conf = {
200 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
201 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
202 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
203 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
204 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900205 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500206 {
207 .pixel_clock = 27000000,
208 .conf = {
209 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
210 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
211 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
212 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
213 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900214 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500215 {
216 .pixel_clock = 27027000,
217 .conf = {
218 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
219 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
220 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200221 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500222 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900223 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500224 {
225 .pixel_clock = 36000000,
226 .conf = {
227 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
228 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
229 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
230 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
231 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900232 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500233 {
234 .pixel_clock = 40000000,
235 .conf = {
236 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
237 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
238 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
239 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
240 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900241 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500242 {
243 .pixel_clock = 65000000,
244 .conf = {
245 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
246 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
247 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
248 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
249 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900250 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500251 {
Shirish Se1d883c2014-03-13 14:28:27 +0900252 .pixel_clock = 71000000,
253 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530254 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
255 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
256 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900257 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
258 },
259 },
260 {
261 .pixel_clock = 73250000,
262 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530263 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
264 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
265 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900266 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
267 },
268 },
269 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500270 .pixel_clock = 74176000,
271 .conf = {
272 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
273 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
274 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
275 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
276 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900277 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500278 {
279 .pixel_clock = 74250000,
280 .conf = {
281 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
282 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
283 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200284 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500285 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900286 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500287 {
288 .pixel_clock = 83500000,
289 .conf = {
290 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
291 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
292 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
293 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
294 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900295 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500296 {
297 .pixel_clock = 106500000,
298 .conf = {
299 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
300 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
301 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
302 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
303 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900304 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500305 {
306 .pixel_clock = 108000000,
307 .conf = {
308 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
309 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
310 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
311 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
312 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900313 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500314 {
Shirish Se1d883c2014-03-13 14:28:27 +0900315 .pixel_clock = 115500000,
316 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530317 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
318 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
319 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900320 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
321 },
322 },
323 {
324 .pixel_clock = 119000000,
325 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530326 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
327 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
328 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900329 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
330 },
331 },
332 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500333 .pixel_clock = 146250000,
334 .conf = {
335 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
336 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
337 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
338 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
339 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900340 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500341 {
342 .pixel_clock = 148500000,
343 .conf = {
344 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
345 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
346 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200347 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500348 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900349 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900350};
351
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530352static const struct hdmiphy_config hdmiphy_5420_configs[] = {
353 {
354 .pixel_clock = 25200000,
355 .conf = {
356 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
357 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
358 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
359 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
360 },
361 },
362 {
363 .pixel_clock = 27000000,
364 .conf = {
365 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
366 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
367 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
368 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
369 },
370 },
371 {
372 .pixel_clock = 27027000,
373 .conf = {
374 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
375 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
376 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
377 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
378 },
379 },
380 {
381 .pixel_clock = 36000000,
382 .conf = {
383 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
384 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
385 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
386 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
387 },
388 },
389 {
390 .pixel_clock = 40000000,
391 .conf = {
392 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
393 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
394 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
395 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
396 },
397 },
398 {
399 .pixel_clock = 65000000,
400 .conf = {
401 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
402 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
403 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
404 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
405 },
406 },
407 {
408 .pixel_clock = 71000000,
409 .conf = {
410 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
411 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
412 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
413 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
414 },
415 },
416 {
417 .pixel_clock = 73250000,
418 .conf = {
419 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
420 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
421 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
422 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
423 },
424 },
425 {
426 .pixel_clock = 74176000,
427 .conf = {
428 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
429 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
430 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
431 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
432 },
433 },
434 {
435 .pixel_clock = 74250000,
436 .conf = {
437 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
438 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
439 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
440 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
441 },
442 },
443 {
444 .pixel_clock = 83500000,
445 .conf = {
446 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
447 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
448 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
449 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
450 },
451 },
452 {
453 .pixel_clock = 88750000,
454 .conf = {
455 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
456 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
457 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
458 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
459 },
460 },
461 {
462 .pixel_clock = 106500000,
463 .conf = {
464 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
465 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
466 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
467 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
468 },
469 },
470 {
471 .pixel_clock = 108000000,
472 .conf = {
473 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
474 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
475 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
476 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
477 },
478 },
479 {
480 .pixel_clock = 115500000,
481 .conf = {
482 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
483 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
484 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
485 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
486 },
487 },
488 {
489 .pixel_clock = 146250000,
490 .conf = {
491 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
492 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
493 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
494 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
495 },
496 },
497 {
498 .pixel_clock = 148500000,
499 .conf = {
500 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
501 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
502 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
503 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
504 },
505 },
506};
507
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900508static const struct hdmiphy_config hdmiphy_5433_configs[] = {
509 {
510 .pixel_clock = 27000000,
511 .conf = {
512 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46,
513 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
514 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
515 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
516 },
517 },
518 {
519 .pixel_clock = 27027000,
520 .conf = {
521 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
522 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5,
523 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
524 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
525 },
526 },
527 {
528 .pixel_clock = 40000000,
529 .conf = {
530 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
531 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
532 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
533 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
534 },
535 },
536 {
537 .pixel_clock = 50000000,
538 .conf = {
539 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
540 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
541 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
542 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
543 },
544 },
545 {
546 .pixel_clock = 65000000,
547 .conf = {
548 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
549 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
550 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
551 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
552 },
553 },
554 {
555 .pixel_clock = 74176000,
556 .conf = {
557 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
558 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
559 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
560 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
561 },
562 },
563 {
564 .pixel_clock = 74250000,
565 .conf = {
566 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
567 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
568 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
569 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
570 },
571 },
572 {
573 .pixel_clock = 108000000,
574 .conf = {
575 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
576 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
577 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
578 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
579 },
580 },
581 {
582 .pixel_clock = 148500000,
583 .conf = {
584 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
585 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
586 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
587 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
588 },
589 },
590};
591
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100592static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900593 "hdmi", "sclk_hdmi"
594};
595
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100596static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900597 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
598};
599
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900600static const char * const hdmi_clk_gates5433[] = {
601 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
602};
603
604static const char * const hdmi_clk_muxes5433[] = {
605 "oscclk", "tmds_clko", "tmds_clko_user",
606 "oscclk", "pixel_clko", "pixel_clko_user"
607};
608
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900609static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
610 .type = HDMI_TYPE13,
611 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900612 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
613 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530614};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900615
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100616static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900617 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100618 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900619 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
620 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900621};
622
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900623static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
624 .type = HDMI_TYPE14,
625 .is_apb_phy = 1,
626 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900627 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
628 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200629};
630
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900631static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
632 .type = HDMI_TYPE14,
633 .is_apb_phy = 1,
634 .has_sysreg = 1,
635 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
636 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
637 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
638};
639
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200640static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
641{
642 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
643 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
644 return reg_id;
645}
646
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900647static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
648{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200649 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900650}
651
652static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
653 u32 reg_id, u8 value)
654{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200655 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900656}
657
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200658static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
659 int bytes, u32 val)
660{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200661 reg_id = hdmi_map_reg(hdata, reg_id);
662
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200663 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200664 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200665 val >>= 8;
666 reg_id += 4;
667 }
668}
669
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100670static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
671 u8 *buf, int size)
672{
673 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
674 writel(*buf++, hdata->regs + reg_id);
675}
676
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900677static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
678 u32 reg_id, u32 value, u32 mask)
679{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200680 u32 old;
681
682 reg_id = hdmi_map_reg(hdata, reg_id);
683 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900684 value = (value & mask) | (old & ~mask);
685 writel(value, hdata->regs + reg_id);
686}
687
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900688static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
689 u32 reg_offset, const u8 *buf, u32 len)
690{
691 if ((reg_offset + len) > 32)
692 return -EINVAL;
693
694 if (hdata->hdmiphy_port) {
695 int ret;
696
697 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
698 if (ret == len)
699 return 0;
700 return ret;
701 } else {
702 int i;
703 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200704 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900705 ((reg_offset + i)<<2));
706 return 0;
707 }
708}
709
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900710static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
711{
712 int i, ret;
713
714 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
715 ret = clk_prepare_enable(hdata->clk_gates[i]);
716 if (!ret)
717 continue;
718
719 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
720 hdata->drv_data->clk_gates.data[i], ret);
721 while (i--)
722 clk_disable_unprepare(hdata->clk_gates[i]);
723 return ret;
724 }
725
726 return 0;
727}
728
729static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
730{
731 int i = hdata->drv_data->clk_gates.count;
732
733 while (i--)
734 clk_disable_unprepare(hdata->clk_gates[i]);
735}
736
737static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
738{
739 struct device *dev = hdata->dev;
740 int ret = 0;
741 int i;
742
743 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
744 struct clk **c = &hdata->clk_muxes[i];
745
746 ret = clk_set_parent(c[2], c[to_phy]);
747 if (!ret)
748 continue;
749
750 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
751 hdata->drv_data->clk_muxes.data[i + 2],
752 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
753 }
754
755 return ret;
756}
757
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100758static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530759{
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100760 union hdmi_infoframe frm;
761 u8 buf[25];
762 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530763
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530764 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530765 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
766 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100767 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
768 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530769 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
770 return;
771 }
772
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100773 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
774 &hdata->current_mode);
775 if (!ret)
776 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
777 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530778 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100779 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
780 } else {
781 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
782 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530783
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100784 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
785 &hdata->current_mode);
786 if (!ret)
787 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
788 sizeof(buf));
789 if (ret > 0) {
790 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
791 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, ret);
792 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530793
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100794 ret = hdmi_audio_infoframe_init(&frm.audio);
795 if (!ret) {
796 frm.audio.channels = 2;
797 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
798 }
799 if (ret > 0) {
800 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
801 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530802 }
803}
804
Sean Pauld9716ee2014-01-30 16:19:29 -0500805static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
806 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500807{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200808 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500809
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200810 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200811 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530812
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200813 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500814}
815
Sean Pauld9716ee2014-01-30 16:19:29 -0500816static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900817{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200818 drm_connector_unregister(connector);
819 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900820}
821
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100822static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300823 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500824 .fill_modes = drm_helper_probe_single_connector_modes,
825 .detect = hdmi_detect,
826 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300827 .reset = drm_atomic_helper_connector_reset,
828 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
829 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500830};
831
832static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900833{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200834 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500835 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200836 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900837
Inki Dae8fa04aa2014-03-13 16:38:31 +0900838 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500839 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900840
Inki Dae8fa04aa2014-03-13 16:38:31 +0900841 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500842 if (!edid)
843 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900844
Sean Pauld9716ee2014-01-30 16:19:29 -0500845 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500846 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
847 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500848 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500849
Sean Pauld9716ee2014-01-30 16:19:29 -0500850 drm_mode_connector_update_edid_property(connector, edid);
851
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200852 ret = drm_add_edid_modes(connector, edid);
853
854 kfree(edid);
855
856 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900857}
858
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900859static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900860{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100861 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900862 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900863
Andrzej Hajda65e98032015-11-02 14:16:41 +0100864 for (i = 0; i < confs->count; i++)
865 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500866 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500867
868 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
869 return -EINVAL;
870}
871
Sean Pauld9716ee2014-01-30 16:19:29 -0500872static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500873 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900874{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200875 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900876 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900877
Rahul Sharma16844fb2013-06-10 14:50:00 +0530878 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
879 mode->hdisplay, mode->vdisplay, mode->vrefresh,
880 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
881 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900882
Rahul Sharma16844fb2013-06-10 14:50:00 +0530883 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900884 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500885 return MODE_BAD;
886
887 return MODE_OK;
888}
889
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100890static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500891 .get_modes = hdmi_get_modes,
892 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500893};
894
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300895static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500896{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300897 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500898 struct drm_connector *connector = &hdata->connector;
899 int ret;
900
Sean Pauld9716ee2014-01-30 16:19:29 -0500901 connector->interlace_allowed = true;
902 connector->polled = DRM_CONNECTOR_POLL_HPD;
903
904 ret = drm_connector_init(hdata->drm_dev, connector,
905 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
906 if (ret) {
907 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900908 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500909 }
910
911 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100912 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300913 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500914
915 return 0;
916}
917
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300918static bool hdmi_mode_fixup(struct drm_encoder *encoder,
919 const struct drm_display_mode *mode,
920 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500921{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300922 struct drm_device *dev = encoder->dev;
923 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500924 struct drm_display_mode *m;
925 int mode_ok;
926
Sean Paulf041b252014-01-30 16:19:15 -0500927 drm_mode_set_crtcinfo(adjusted_mode, 0);
928
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300929 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
930 if (connector->encoder == encoder)
931 break;
932 }
933
934 if (connector->encoder != encoder)
935 return true;
936
Sean Pauld9716ee2014-01-30 16:19:29 -0500937 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500938
Sean Pauld9716ee2014-01-30 16:19:29 -0500939 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300940 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500941
942 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900943 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500944 */
945 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500946 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500947
Sean Pauld9716ee2014-01-30 16:19:29 -0500948 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500949 DRM_INFO("desired mode doesn't exist so\n");
950 DRM_INFO("use the most suitable mode among modes.\n");
951
952 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
953 m->hdisplay, m->vdisplay, m->vrefresh);
954
Sean Paul75626852014-01-30 16:19:16 -0500955 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500956 break;
957 }
958 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300959
960 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500961}
962
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200963static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900964{
965 u32 n, cts;
966
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200967 cts = (freq % 9) ? 27000 : 30000;
968 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900969
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200970 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
971 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
972 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200973 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900974}
975
976static void hdmi_audio_init(struct hdmi_context *hdata)
977{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +0530978 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900979 u32 data_num, bit_ch, sample_frq;
980 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900981
982 sample_rate = 44100;
983 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900984
985 switch (bits_per_sample) {
986 case 20:
987 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900988 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900989 break;
990 case 24:
991 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900992 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900993 break;
994 default:
995 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900996 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900997 break;
998 }
999
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001000 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001001
1002 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1003 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1004 | HDMI_I2S_MUX_ENABLE);
1005
1006 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1007 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1008
1009 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1010
1011 sample_frq = (sample_rate == 44100) ? 0 :
1012 (sample_rate == 48000) ? 2 :
1013 (sample_rate == 32000) ? 3 :
1014 (sample_rate == 96000) ? 0xa : 0x0;
1015
1016 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1017 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1018
1019 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1020 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1021
1022 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1023 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1024 | HDMI_I2S_SEL_LRCK(6));
1025 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1026 | HDMI_I2S_SEL_SDATA2(4));
1027 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1028 | HDMI_I2S_SEL_SDATA2(2));
1029 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1030
1031 /* I2S_CON_1 & 2 */
1032 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1033 | HDMI_I2S_L_CH_LOW_POL);
1034 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1035 | HDMI_I2S_SET_BIT_CH(bit_ch)
1036 | HDMI_I2S_SET_SDATA_BIT(data_num)
1037 | HDMI_I2S_BASIC_FORMAT);
1038
1039 /* Configure register related to CUV information */
1040 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1041 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1042 | HDMI_I2S_COPYRIGHT
1043 | HDMI_I2S_LINEAR_PCM
1044 | HDMI_I2S_CONSUMER_FORMAT);
1045 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1046 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1047 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1048 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1049 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1050 HDMI_I2S_ORG_SMP_FREQ_44_1
1051 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1052 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1053
1054 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1055}
1056
1057static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1058{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001059 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001060 return;
1061
1062 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1063 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1064 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1065}
1066
Rahul Sharmabfa48422014-04-03 20:41:04 +05301067static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001068{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301069 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001070
Rahul Sharmabfa48422014-04-03 20:41:04 +05301071 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1072 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001073
Rahul Sharmabfa48422014-04-03 20:41:04 +05301074 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1075 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001076}
1077
1078static void hdmi_conf_init(struct hdmi_context *hdata)
1079{
Sean Paul77006a72013-01-16 10:17:20 -05001080 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001081 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1082 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001083
1084 /* choose HDMI mode */
1085 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1086 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001087 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301088 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001089 /* disable bluescreen */
1090 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001091
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001092 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001093 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1094 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1095 hdmi_reg_writeb(hdata, HDMI_CON_2,
1096 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1097 }
1098
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001099 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001100 /* choose bluescreen (fecal) color */
1101 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1102 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1103 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1104
1105 /* enable AVI packet every vsync, fixes purple line problem */
1106 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1107 /* force RGB, look to CEA-861-D, table 7 for more detail */
1108 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1109 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1110
1111 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1112 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1113 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1114 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001115 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301116
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001117 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001118 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1119 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001120}
1121
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001122static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1123{
1124 int tries;
1125
1126 for (tries = 0; tries < 10; ++tries) {
1127 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1128
1129 if (val & HDMI_PHY_STATUS_READY) {
1130 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1131 return;
1132 }
1133 usleep_range(10, 20);
1134 }
1135
1136 DRM_ERROR("PLL could not reach steady state\n");
1137}
1138
Rahul Sharma16844fb2013-06-10 14:50:00 +05301139static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001140{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001141 struct drm_display_mode *m = &hdata->current_mode;
1142 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001143
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001144 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1145 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1146 (m->htotal << 12) | m->vtotal);
1147
1148 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1149 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1150
1151 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1152 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1153
1154 val = (m->hsync_start - m->hdisplay - 2);
1155 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001156 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001157 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1158
1159 /*
1160 * Quirk requirement for exynos HDMI IP design,
1161 * 2 pixels less than the actual calculation for hsync_start
1162 * and end.
1163 */
1164
1165 /* Following values & calculations differ for different type of modes */
1166 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001167 val = ((m->vsync_end - m->vdisplay) / 2);
1168 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1169 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1170
1171 val = m->vtotal / 2;
1172 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1173 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1174
1175 val = (m->vtotal +
1176 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1177 val |= m->vtotal << 11;
1178 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1179
1180 val = ((m->vtotal / 2) + 7);
1181 val |= ((m->vtotal / 2) + 2) << 12;
1182 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1183
1184 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1185 val |= ((m->htotal / 2) +
1186 (m->hsync_start - m->hdisplay)) << 12;
1187 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1188
1189 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1190 (m->vtotal - m->vdisplay) / 2);
1191 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1192
1193 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1194 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001195 val = m->vtotal;
1196 val |= (m->vtotal - m->vdisplay) << 11;
1197 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1198
1199 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1200
1201 val = (m->vsync_end - m->vdisplay);
1202 val |= ((m->vsync_start - m->vdisplay) << 12);
1203 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1204
1205 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1206 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1207 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1208 m->vtotal - m->vdisplay);
1209 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001210 }
1211
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001212 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1213 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1214 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1215 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001216}
1217
Rahul Sharma16844fb2013-06-10 14:50:00 +05301218static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001219{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001220 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001221
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001222 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1223 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1224 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1225 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001226 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001227 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1228 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1229 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1230 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1231
1232 /*
1233 * Quirk requirement for exynos 5 HDMI IP design,
1234 * 2 pixels less than the actual calculation for hsync_start
1235 * and end.
1236 */
1237
1238 /* Following values & calculations differ for different type of modes */
1239 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001240 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1241 (m->vsync_end - m->vdisplay) / 2);
1242 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1243 (m->vsync_start - m->vdisplay) / 2);
1244 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1245 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1246 (m->vtotal - m->vdisplay) / 2);
1247 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1248 m->vtotal - m->vdisplay / 2);
1249 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1250 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1251 (m->vtotal / 2) + 7);
1252 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1253 (m->vtotal / 2) + 2);
1254 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1255 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1256 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1257 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1258 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1259 (m->vtotal - m->vdisplay) / 2);
1260 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1261 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1262 m->vtotal - m->vdisplay / 2);
1263 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1264 (m->vtotal / 2) + 1);
1265 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1266 (m->vtotal / 2) + 1);
1267 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1268 (m->vtotal / 2) + 1);
1269 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1270 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1271 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001272 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1273 m->vsync_end - m->vdisplay);
1274 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1275 m->vsync_start - m->vdisplay);
1276 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1277 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1278 m->vtotal - m->vdisplay);
1279 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1280 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1281 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1282 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1283 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1284 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1285 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1286 m->vtotal - m->vdisplay);
1287 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001288 }
1289
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001290 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1291 m->hsync_start - m->hdisplay - 2);
1292 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1293 m->hsync_end - m->hdisplay - 2);
1294 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1295 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1296 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1297 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1298 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1299 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1300 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1301 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1302 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1303 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1304 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1306 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1307 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1308 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1309 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1310 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1311 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001312
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001313 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1314 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1315 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1316 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001317 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1318 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001319}
1320
Rahul Sharma16844fb2013-06-10 14:50:00 +05301321static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001322{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001323 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301324 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001325 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301326 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001327
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001328 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001329}
1330
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001331static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1332{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001333 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1334 usleep_range(10000, 12000);
1335 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1336 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001337 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001338 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001339 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001340 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001341}
1342
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001343static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1344{
1345 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1346
1347 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1348 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1349}
1350
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001351static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1352{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001353 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001354 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001355
Andrzej Hajda4677f512016-03-23 14:15:12 +01001356 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1357 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001358 DRM_ERROR("failed to find hdmiphy conf\n");
1359 return;
1360 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001361 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1362
1363 hdmi_clk_set_parents(hdata, false);
1364
1365 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001366
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001367 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001368 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001369 if (ret) {
1370 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001371 return;
1372 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001373 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001374 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001375 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001376 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001377}
1378
1379static void hdmi_conf_apply(struct hdmi_context *hdata)
1380{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301381 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001382 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001383 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301384 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001385 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001386}
1387
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001388static void hdmi_mode_set(struct drm_encoder *encoder,
1389 struct drm_display_mode *mode,
1390 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001391{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001392 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001393 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001394
YoungJun Chocbc4c332013-06-12 10:44:40 +09001395 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1396 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001397 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001398 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001399
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001400 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001401}
1402
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001403static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1404{
1405 if (!hdata->sysreg)
1406 return;
1407
1408 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1409 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1410}
1411
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001412static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001413{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001414 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001415 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001416
Sean Paulaf65c802014-01-30 16:19:27 -05001417 pm_runtime_get_sync(hdata->dev);
1418
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001419 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001420 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1421
Rahul Sharma049d34e2014-05-20 10:36:05 +05301422 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1423 PMU_HDMI_PHY_ENABLE_BIT, 1);
1424
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001425 hdmi_set_refclk(hdata, true);
1426
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001427 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1428
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001429 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001430
1431 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001432}
1433
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001434static void hdmiphy_disable(struct hdmi_context *hdata)
1435{
1436 if (!hdata->powered)
1437 return;
1438
1439 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1440
1441 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1442
1443 hdmi_set_refclk(hdata, false);
1444
1445 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1446 PMU_HDMI_PHY_ENABLE_BIT, 0);
1447
1448 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1449
1450 pm_runtime_put_sync(hdata->dev);
1451
1452 hdata->powered = false;
1453}
1454
1455static void hdmi_enable(struct drm_encoder *encoder)
1456{
1457 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1458
1459 hdmiphy_enable(hdata);
1460 hdmi_conf_apply(hdata);
1461}
1462
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001463static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001464{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001465 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001466 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001467 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001468
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001469 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001470 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001471
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001472 /*
1473 * The SFRs of VP and Mixer are updated by Vertical Sync of
1474 * Timing generator which is a part of HDMI so the sequence
1475 * to disable TV Subsystem should be as following,
1476 * VP -> Mixer -> HDMI
1477 *
1478 * Below codes will try to disable Mixer and VP(if used)
1479 * prior to disabling HDMI.
1480 */
1481 if (crtc)
1482 funcs = crtc->helper_private;
1483 if (funcs && funcs->disable)
1484 (*funcs->disable)(crtc);
1485
Sean Paul724fd142014-05-09 15:05:10 +09001486 cancel_delayed_work(&hdata->hotplug_work);
1487
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001488 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001489}
1490
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001491static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001492 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001493 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001494 .enable = hdmi_enable,
1495 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001496};
1497
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001498static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001499 .destroy = drm_encoder_cleanup,
1500};
1501
Sean Paul724fd142014-05-09 15:05:10 +09001502static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001503{
Sean Paul724fd142014-05-09 15:05:10 +09001504 struct hdmi_context *hdata;
1505
1506 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001507
Sean Paul45517892014-01-30 16:19:05 -05001508 if (hdata->drm_dev)
1509 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001510}
1511
1512static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1513{
1514 struct hdmi_context *hdata = arg;
1515
1516 mod_delayed_work(system_wq, &hdata->hotplug_work,
1517 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001518
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001519 return IRQ_HANDLED;
1520}
1521
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001522static int hdmi_clks_get(struct hdmi_context *hdata,
1523 const struct string_array_spec *names,
1524 struct clk **clks)
1525{
1526 struct device *dev = hdata->dev;
1527 int i;
1528
1529 for (i = 0; i < names->count; ++i) {
1530 struct clk *clk = devm_clk_get(dev, names->data[i]);
1531
1532 if (IS_ERR(clk)) {
1533 int ret = PTR_ERR(clk);
1534
1535 dev_err(dev, "Cannot get clock %s, %d\n",
1536 names->data[i], ret);
1537
1538 return ret;
1539 }
1540
1541 clks[i] = clk;
1542 }
1543
1544 return 0;
1545}
1546
1547static int hdmi_clk_init(struct hdmi_context *hdata)
1548{
1549 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1550 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1551 struct device *dev = hdata->dev;
1552 struct clk **clks;
1553 int ret;
1554
1555 if (!count)
1556 return 0;
1557
1558 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1559 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001560 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001561
1562 hdata->clk_gates = clks;
1563 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1564
1565 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1566 if (ret)
1567 return ret;
1568
1569 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1570}
1571
1572
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001573static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1574{
1575 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1576 phy_clk);
1577
1578 if (enable)
1579 hdmiphy_enable(hdata);
1580 else
1581 hdmiphy_disable(hdata);
1582}
1583
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001584static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001585{
1586 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001587 int i, ret;
1588
1589 DRM_DEBUG_KMS("HDMI resource init\n");
1590
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001591 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1592 if (IS_ERR(hdata->hpd_gpio)) {
1593 DRM_ERROR("cannot get hpd gpio property\n");
1594 return PTR_ERR(hdata->hpd_gpio);
1595 }
1596
1597 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1598 if (hdata->irq < 0) {
1599 DRM_ERROR("failed to get GPIO irq\n");
1600 return hdata->irq;
1601 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001602
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001603 ret = hdmi_clk_init(hdata);
1604 if (ret)
1605 return ret;
1606
1607 ret = hdmi_clk_set_parents(hdata, false);
1608 if (ret)
1609 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001610
Milo Kimc0d656d2016-08-31 15:14:27 +09001611 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001612 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001613
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001614 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001615 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001616 if (ret != -EPROBE_DEFER)
1617 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001618 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001619 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001620
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001621 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001622
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001623 if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001624 return 0;
1625
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001626 if (IS_ERR(hdata->reg_hdmi_en))
1627 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001628
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001629 ret = regulator_enable(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001630 if (ret)
1631 DRM_ERROR("failed to enable hdmi-en regulator\n");
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001632
Inki Daedf5225b2014-05-29 18:28:02 +09001633 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001634}
1635
Rahul Sharma22c4f422012-10-04 20:48:55 +05301636static struct of_device_id hdmi_match_types[] = {
1637 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001638 .compatible = "samsung,exynos4210-hdmi",
1639 .data = &exynos4210_hdmi_driver_data,
1640 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301641 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001642 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301643 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301644 .compatible = "samsung,exynos5420-hdmi",
1645 .data = &exynos5420_hdmi_driver_data,
1646 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001647 .compatible = "samsung,exynos5433-hdmi",
1648 .data = &exynos5433_hdmi_driver_data,
1649 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301650 /* end node */
1651 }
1652};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001653MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301654
Inki Daef37cd5e2014-05-09 14:25:20 +09001655static int hdmi_bind(struct device *dev, struct device *master, void *data)
1656{
1657 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001658 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001659 struct drm_encoder *encoder = &hdata->encoder;
1660 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001661
Inki Daef37cd5e2014-05-09 14:25:20 +09001662 hdata->drm_dev = drm_dev;
1663
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001664 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1665 EXYNOS_DISPLAY_TYPE_HDMI);
1666 if (pipe < 0)
1667 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001668
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001669 hdata->phy_clk.enable = hdmiphy_clk_enable;
1670
1671 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1672
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001673 encoder->possible_crtcs = 1 << pipe;
1674
1675 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1676
1677 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001678 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001679
1680 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1681
1682 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001683 if (ret) {
1684 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001685 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001686 return ret;
1687 }
1688
1689 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001690}
1691
1692static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1693{
Inki Daef37cd5e2014-05-09 14:25:20 +09001694}
1695
1696static const struct component_ops hdmi_component_ops = {
1697 .bind = hdmi_bind,
1698 .unbind = hdmi_unbind,
1699};
1700
Milo Kim1caa3602016-08-31 15:14:25 +09001701static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001702{
1703 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1704 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001705 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001706
1707 np = of_find_compatible_node(NULL, NULL, compatible_str);
1708 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001709 np = of_get_next_parent(np);
1710 else
1711 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001712
Milo Kim1caa3602016-08-31 15:14:25 +09001713 if (!np) {
1714 DRM_ERROR("Failed to find ddc node in device tree\n");
1715 return -ENODEV;
1716 }
1717
1718 adpt = of_find_i2c_adapter_by_node(np);
1719 of_node_put(np);
1720
1721 if (!adpt) {
1722 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1723 return -EPROBE_DEFER;
1724 }
1725
1726 hdata->ddc_adpt = adpt;
1727
1728 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001729}
1730
Milo Kimb5413022016-08-31 15:14:26 +09001731static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001732{
1733 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001734 struct device_node *np;
1735 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001736
Milo Kimb5413022016-08-31 15:14:26 +09001737 np = of_find_compatible_node(NULL, NULL, compatible_str);
1738 if (!np) {
1739 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1740 if (!np) {
1741 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1742 return -ENODEV;
1743 }
1744 }
1745
1746 if (hdata->drv_data->is_apb_phy) {
1747 hdata->regs_hdmiphy = of_iomap(np, 0);
1748 if (!hdata->regs_hdmiphy) {
1749 DRM_ERROR("failed to ioremap hdmi phy\n");
1750 ret = -ENOMEM;
1751 goto out;
1752 }
1753 } else {
1754 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1755 if (!hdata->hdmiphy_port) {
1756 DRM_INFO("Failed to get hdmi phy i2c client\n");
1757 ret = -EPROBE_DEFER;
1758 goto out;
1759 }
1760 }
1761
1762out:
1763 of_node_put(np);
1764 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001765}
1766
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001767static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001768{
1769 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001770 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001771 struct resource *res;
1772 int ret;
1773
Andrzej Hajda930865f2014-11-17 09:54:20 +01001774 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1775 if (!hdata)
1776 return -ENOMEM;
1777
Marek Szyprowski57a64122016-04-01 15:17:44 +02001778 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001779
Andrzej Hajda930865f2014-11-17 09:54:20 +01001780 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001781
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001782 hdata->dev = dev;
1783
1784 ret = hdmi_resources_init(hdata);
1785 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001786 if (ret != -EPROBE_DEFER)
1787 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001788 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001789 }
1790
1791 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001792 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001793 if (IS_ERR(hdata->regs)) {
1794 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001795 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001796 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001797
Milo Kim1caa3602016-08-31 15:14:25 +09001798 ret = hdmi_get_ddc_adapter(hdata);
1799 if (ret)
1800 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001801
Milo Kimb5413022016-08-31 15:14:26 +09001802 ret = hdmi_get_phy_io(hdata);
1803 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001804 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001805
Sean Paul724fd142014-05-09 15:05:10 +09001806 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1807
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001808 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001809 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001810 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001811 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001812 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001813 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001814 goto err_hdmiphy;
1815 }
1816
Rahul Sharma049d34e2014-05-20 10:36:05 +05301817 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1818 "samsung,syscon-phandle");
1819 if (IS_ERR(hdata->pmureg)) {
1820 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001821 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301822 goto err_hdmiphy;
1823 }
1824
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001825 if (hdata->drv_data->has_sysreg) {
1826 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1827 "samsung,sysreg-phandle");
1828 if (IS_ERR(hdata->sysreg)) {
1829 DRM_ERROR("sysreg regmap lookup failed.\n");
1830 ret = -EPROBE_DEFER;
1831 goto err_hdmiphy;
1832 }
1833 }
1834
Sean Paulaf65c802014-01-30 16:19:27 -05001835 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001836
Inki Daedf5225b2014-05-29 18:28:02 +09001837 ret = component_add(&pdev->dev, &hdmi_component_ops);
1838 if (ret)
1839 goto err_disable_pm_runtime;
1840
1841 return ret;
1842
1843err_disable_pm_runtime:
1844 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001845
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001846err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001847 if (hdata->hdmiphy_port)
1848 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301849 if (hdata->regs_hdmiphy)
1850 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001851err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001852 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001853
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001854 return ret;
1855}
1856
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001857static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001858{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001859 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001860
Sean Paul724fd142014-05-09 15:05:10 +09001861 cancel_delayed_work_sync(&hdata->hotplug_work);
1862
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001863 component_del(&pdev->dev, &hdmi_component_ops);
1864
1865 pm_runtime_disable(&pdev->dev);
1866
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001867 if (!IS_ERR(hdata->reg_hdmi_en))
1868 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001869
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001870 if (hdata->hdmiphy_port)
1871 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001872
Arvind Yadavd7420002016-10-19 15:34:16 +05301873 if (hdata->regs_hdmiphy)
1874 iounmap(hdata->regs_hdmiphy);
1875
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001876 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001877
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001878 return 0;
1879}
1880
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001881#ifdef CONFIG_PM
1882static int exynos_hdmi_suspend(struct device *dev)
1883{
1884 struct hdmi_context *hdata = dev_get_drvdata(dev);
1885
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001886 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001887
1888 return 0;
1889}
1890
1891static int exynos_hdmi_resume(struct device *dev)
1892{
1893 struct hdmi_context *hdata = dev_get_drvdata(dev);
1894 int ret;
1895
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001896 ret = hdmi_clk_enable_gates(hdata);
1897 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001898 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001899
1900 return 0;
1901}
1902#endif
1903
1904static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1905 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1906};
1907
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001908struct platform_driver hdmi_driver = {
1909 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001910 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001911 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301912 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001913 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001914 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301915 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001916 },
1917};