blob: 88ccc0469316b191d75f87de57ccad301dc42145 [file] [log] [blame]
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
Andrzej Hajda5eefadb2016-01-14 14:28:20 +090010 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
Seung-Woo Kimd8408322011-12-21 17:39:39 +090013 * option) any later version.
14 *
15 */
16
David Howells760285e2012-10-02 18:01:07 +010017#include <drm/drmP.h>
18#include <drm/drm_edid.h>
19#include <drm/drm_crtc_helper.h>
Gustavo Padovan4ea95262015-06-01 12:04:44 -030020#include <drm/drm_atomic_helper.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090021
22#include "regs-hdmi.h"
23
24#include <linux/kernel.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090025#include <linux/wait.h>
26#include <linux/i2c.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090027#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
Andrzej Hajda2228b7c2015-09-25 14:48:24 +020033#include <linux/gpio/consumer.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090034#include <linux/regulator/consumer.h>
Rahul Sharma22c4f422012-10-04 20:48:55 +053035#include <linux/io.h>
Rahul Sharmad5e9ca42014-05-09 15:34:18 +090036#include <linux/of_address.h>
Andrzej Hajdacd240cd2015-07-09 16:28:09 +020037#include <linux/of_device.h>
Andrzej Hajdaaa181572017-02-01 09:29:14 +010038#include <linux/of_graph.h>
Sachin Kamatd34d59b2014-02-04 08:40:18 +053039#include <linux/hdmi.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090040#include <linux/component.h>
Rahul Sharma049d34e2014-05-20 10:36:05 +053041#include <linux/mfd/syscon.h>
42#include <linux/regmap.h>
Seung-Woo Kimd8408322011-12-21 17:39:39 +090043
44#include <drm/exynos_drm.h>
45
46#include "exynos_drm_drv.h"
Inki Daef37cd5e2014-05-09 14:25:20 +090047#include "exynos_drm_crtc.h"
Seung-Woo Kimd8408322011-12-21 17:39:39 +090048
Sean Paul724fd142014-05-09 15:05:10 +090049#define HOTPLUG_DEBOUNCE_MS 1100
50
Rahul Sharma5a325072012-10-04 20:48:54 +053051enum hdmi_type {
52 HDMI_TYPE13,
53 HDMI_TYPE14,
Andrzej Hajda633d00b2015-09-25 14:48:16 +020054 HDMI_TYPE_COUNT
55};
56
57#define HDMI_MAPPED_BASE 0xffff0000
58
59enum hdmi_mapped_regs {
60 HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
61 HDMI_PHY_RSTOUT,
62 HDMI_ACR_CON,
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020063 HDMI_ACR_MCTS0,
64 HDMI_ACR_CTS0,
65 HDMI_ACR_N0
Andrzej Hajda633d00b2015-09-25 14:48:16 +020066};
67
68static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
69 { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
70 { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
71 { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +020072 { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
73 { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
74 { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
Rahul Sharma5a325072012-10-04 20:48:54 +053075};
76
Andrzej Hajda1ab739d2015-09-25 14:48:22 +020077static const char * const supply[] = {
78 "vdd",
79 "vdd_osc",
80 "vdd_pll",
81};
82
Andrzej Hajda65e98032015-11-02 14:16:41 +010083struct hdmiphy_config {
84 int pixel_clock;
85 u8 conf[32];
86};
87
88struct hdmiphy_configs {
89 int count;
90 const struct hdmiphy_config *data;
91};
92
Andrzej Hajda9be7e982016-01-14 14:22:47 +090093struct string_array_spec {
94 int count;
95 const char * const *data;
96};
97
98#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
99
Inki Daebfe4e842014-03-06 14:18:17 +0900100struct hdmi_driver_data {
101 unsigned int type;
102 unsigned int is_apb_phy:1;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900103 unsigned int has_sysreg:1;
Andrzej Hajda65e98032015-11-02 14:16:41 +0100104 struct hdmiphy_configs phy_confs;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900105 struct string_array_spec clk_gates;
106 /*
107 * Array of triplets (p_off, p_on, clock), where p_off and p_on are
108 * required parents of clock when HDMI-PHY is respectively off or on.
109 */
110 struct string_array_spec clk_muxes;
Inki Daebfe4e842014-03-06 14:18:17 +0900111};
112
Joonyoung Shim590f4182012-03-16 18:47:14 +0900113struct hdmi_context {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300114 struct drm_encoder encoder;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900115 struct device *dev;
116 struct drm_device *drm_dev;
Sean Pauld9716ee2014-01-30 16:19:29 -0500117 struct drm_connector connector;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +0900118 bool powered;
Seung-Woo Kim872d20d62012-04-24 17:39:15 +0900119 bool dvi_mode;
Sean Paul724fd142014-05-09 15:05:10 +0900120 struct delayed_work hotplug_work;
Rahul Sharmabfa48422014-04-03 20:41:04 +0530121 struct drm_display_mode current_mode;
Andrzej Hajdacd240cd2015-07-09 16:28:09 +0200122 const struct hdmi_driver_data *drv_data;
Joonyoung Shim7ecd34e2012-04-23 19:35:47 +0900123
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200124 void __iomem *regs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900125 void __iomem *regs_hdmiphy;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200126 struct i2c_client *hdmiphy_port;
127 struct i2c_adapter *ddc_adpt;
Gustavo Padovanf28464c2015-11-02 20:39:18 +0900128 struct gpio_desc *hpd_gpio;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200129 int irq;
Rahul Sharma049d34e2014-05-20 10:36:05 +0530130 struct regmap *pmureg;
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900131 struct regmap *sysreg;
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900132 struct clk **clk_gates;
133 struct clk **clk_muxes;
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +0200134 struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
135 struct regulator *reg_hdmi_en;
Andrzej Hajda59b62d32016-05-10 13:56:32 +0900136 struct exynos_drm_clk phy_clk;
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100137 struct drm_bridge *bridge;
Joonyoung Shim590f4182012-03-16 18:47:14 +0900138};
139
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300140static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100141{
Gustavo Padovancf67cc92015-08-11 17:38:06 +0900142 return container_of(e, struct hdmi_context, encoder);
Andrzej Hajda0d8424f82014-11-17 09:54:21 +0100143}
144
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200145static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
146{
147 return container_of(c, struct hdmi_context, connector);
148}
149
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900150static const struct hdmiphy_config hdmiphy_v13_configs[] = {
151 {
152 .pixel_clock = 27000000,
153 .conf = {
154 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
155 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
156 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200157 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900158 },
159 },
160 {
161 .pixel_clock = 27027000,
162 .conf = {
163 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
164 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
165 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200166 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900167 },
168 },
169 {
170 .pixel_clock = 74176000,
171 .conf = {
172 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
173 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
174 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200175 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900176 },
177 },
178 {
179 .pixel_clock = 74250000,
180 .conf = {
181 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
182 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
183 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200184 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900185 },
186 },
187 {
188 .pixel_clock = 148500000,
189 .conf = {
190 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
191 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
192 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200193 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900194 },
195 },
196};
197
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500198static const struct hdmiphy_config hdmiphy_v14_configs[] = {
199 {
200 .pixel_clock = 25200000,
201 .conf = {
202 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
203 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
204 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
205 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
206 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900207 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500208 {
209 .pixel_clock = 27000000,
210 .conf = {
211 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
212 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
213 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
214 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
215 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900216 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500217 {
218 .pixel_clock = 27027000,
219 .conf = {
220 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
221 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
222 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200223 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500224 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900225 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500226 {
227 .pixel_clock = 36000000,
228 .conf = {
229 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
230 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
231 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
232 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
233 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900234 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500235 {
236 .pixel_clock = 40000000,
237 .conf = {
238 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
239 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
240 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
241 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
242 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900243 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500244 {
245 .pixel_clock = 65000000,
246 .conf = {
247 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
248 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
249 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
250 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
251 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900252 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500253 {
Shirish Se1d883c2014-03-13 14:28:27 +0900254 .pixel_clock = 71000000,
255 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530256 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
257 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
258 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900259 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
260 },
261 },
262 {
263 .pixel_clock = 73250000,
264 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530265 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
266 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
267 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900268 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
269 },
270 },
271 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500272 .pixel_clock = 74176000,
273 .conf = {
274 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
275 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
276 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
277 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
278 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900279 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500280 {
281 .pixel_clock = 74250000,
282 .conf = {
283 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
284 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
285 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200286 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500287 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900288 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500289 {
290 .pixel_clock = 83500000,
291 .conf = {
292 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
293 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
294 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
295 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
296 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900297 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500298 {
299 .pixel_clock = 106500000,
300 .conf = {
301 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
302 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
303 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
304 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
305 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900306 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500307 {
308 .pixel_clock = 108000000,
309 .conf = {
310 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
311 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
312 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
313 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
314 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900315 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500316 {
Shirish Se1d883c2014-03-13 14:28:27 +0900317 .pixel_clock = 115500000,
318 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530319 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
320 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
321 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900322 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
323 },
324 },
325 {
326 .pixel_clock = 119000000,
327 .conf = {
Shirish S96d26532014-05-05 10:27:51 +0530328 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
329 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
330 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Shirish Se1d883c2014-03-13 14:28:27 +0900331 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
332 },
333 },
334 {
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500335 .pixel_clock = 146250000,
336 .conf = {
337 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
338 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
339 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
340 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
341 },
Seung-Woo Kime540adf2012-04-24 17:55:06 +0900342 },
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500343 {
344 .pixel_clock = 148500000,
345 .conf = {
346 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
347 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
348 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
Andrzej Hajda74a74ff2015-09-25 14:48:18 +0200349 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500350 },
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900351 },
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900352};
353
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530354static const struct hdmiphy_config hdmiphy_5420_configs[] = {
355 {
356 .pixel_clock = 25200000,
357 .conf = {
358 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
359 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
360 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
361 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
362 },
363 },
364 {
365 .pixel_clock = 27000000,
366 .conf = {
367 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
368 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
369 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
370 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
371 },
372 },
373 {
374 .pixel_clock = 27027000,
375 .conf = {
376 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
377 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
378 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
379 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
380 },
381 },
382 {
383 .pixel_clock = 36000000,
384 .conf = {
385 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
386 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
387 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
388 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
389 },
390 },
391 {
392 .pixel_clock = 40000000,
393 .conf = {
394 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
395 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
396 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
397 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
398 },
399 },
400 {
401 .pixel_clock = 65000000,
402 .conf = {
403 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
404 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
405 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
406 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
407 },
408 },
409 {
410 .pixel_clock = 71000000,
411 .conf = {
412 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
413 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
414 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
415 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
416 },
417 },
418 {
419 .pixel_clock = 73250000,
420 .conf = {
421 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
422 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
423 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
424 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
425 },
426 },
427 {
428 .pixel_clock = 74176000,
429 .conf = {
430 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
431 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
432 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
433 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
434 },
435 },
436 {
437 .pixel_clock = 74250000,
438 .conf = {
439 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
440 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
441 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
442 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
443 },
444 },
445 {
446 .pixel_clock = 83500000,
447 .conf = {
448 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
449 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
450 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
451 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
452 },
453 },
454 {
455 .pixel_clock = 88750000,
456 .conf = {
457 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
458 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
459 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
460 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
461 },
462 },
463 {
464 .pixel_clock = 106500000,
465 .conf = {
466 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
467 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
468 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
469 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
470 },
471 },
472 {
473 .pixel_clock = 108000000,
474 .conf = {
475 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
476 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
477 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
478 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
479 },
480 },
481 {
482 .pixel_clock = 115500000,
483 .conf = {
484 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
485 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
486 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
487 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
488 },
489 },
490 {
491 .pixel_clock = 146250000,
492 .conf = {
493 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
494 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
495 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
496 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
497 },
498 },
499 {
500 .pixel_clock = 148500000,
501 .conf = {
502 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
503 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
504 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
505 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
506 },
507 },
508};
509
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900510static const struct hdmiphy_config hdmiphy_5433_configs[] = {
511 {
512 .pixel_clock = 27000000,
513 .conf = {
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100514 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02,
515 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
516 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900517 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
518 },
519 },
520 {
521 .pixel_clock = 27027000,
522 .conf = {
523 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
Andrzej Hajda849fb0d2017-01-20 07:52:21 +0100524 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac,
525 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
526 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900527 },
528 },
529 {
530 .pixel_clock = 40000000,
531 .conf = {
532 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
533 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
534 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
535 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
536 },
537 },
538 {
539 .pixel_clock = 50000000,
540 .conf = {
541 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
542 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
543 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
544 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
545 },
546 },
547 {
548 .pixel_clock = 65000000,
549 .conf = {
550 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
551 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
552 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
553 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
554 },
555 },
556 {
557 .pixel_clock = 74176000,
558 .conf = {
559 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
560 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
561 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
562 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
563 },
564 },
565 {
566 .pixel_clock = 74250000,
567 .conf = {
568 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
569 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
570 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
571 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
572 },
573 },
574 {
575 .pixel_clock = 108000000,
576 .conf = {
577 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
578 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
579 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
580 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
581 },
582 },
583 {
584 .pixel_clock = 148500000,
585 .conf = {
586 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
587 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
588 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
589 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
590 },
591 },
Andrzej Hajda64822582017-01-20 07:52:19 +0100592 {
593 .pixel_clock = 297000000,
594 .conf = {
595 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2,
596 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
597 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
598 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
599 },
600 },
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900601};
602
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100603static const char * const hdmi_clk_gates4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900604 "hdmi", "sclk_hdmi"
605};
606
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100607static const char * const hdmi_clk_muxes4[] = {
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900608 "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
609};
610
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900611static const char * const hdmi_clk_gates5433[] = {
612 "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
613};
614
615static const char * const hdmi_clk_muxes5433[] = {
616 "oscclk", "tmds_clko", "tmds_clko_user",
617 "oscclk", "pixel_clko", "pixel_clko_user"
618};
619
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900620static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
621 .type = HDMI_TYPE13,
622 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900623 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
624 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +0530625};
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900626
Andrzej Hajda190a3c62015-11-02 14:16:40 +0100627static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900628 .type = HDMI_TYPE14,
Andrzej Hajda65e98032015-11-02 14:16:41 +0100629 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900630 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
631 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900632};
633
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900634static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
635 .type = HDMI_TYPE14,
636 .is_apb_phy = 1,
637 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900638 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
639 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
Marek Szyprowskiff830c92014-07-01 10:10:07 +0200640};
641
Andrzej Hajda68cd0042016-01-14 14:40:07 +0900642static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
643 .type = HDMI_TYPE14,
644 .is_apb_phy = 1,
645 .has_sysreg = 1,
646 .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
647 .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
648 .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
649};
650
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200651static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
652{
653 if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
654 return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
655 return reg_id;
656}
657
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900658static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
659{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200660 return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900661}
662
663static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
664 u32 reg_id, u8 value)
665{
Andrzej Hajda1993c332015-09-25 14:48:19 +0200666 writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900667}
668
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200669static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
670 int bytes, u32 val)
671{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200672 reg_id = hdmi_map_reg(hdata, reg_id);
673
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200674 while (--bytes >= 0) {
Andrzej Hajda1993c332015-09-25 14:48:19 +0200675 writel(val & 0xff, hdata->regs + reg_id);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +0200676 val >>= 8;
677 reg_id += 4;
678 }
679}
680
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100681static inline void hdmi_reg_write_buf(struct hdmi_context *hdata, u32 reg_id,
682 u8 *buf, int size)
683{
684 for (reg_id = hdmi_map_reg(hdata, reg_id); size; --size, reg_id += 4)
685 writel(*buf++, hdata->regs + reg_id);
686}
687
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900688static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
689 u32 reg_id, u32 value, u32 mask)
690{
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200691 u32 old;
692
693 reg_id = hdmi_map_reg(hdata, reg_id);
694 old = readl(hdata->regs + reg_id);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900695 value = (value & mask) | (old & ~mask);
696 writel(value, hdata->regs + reg_id);
697}
698
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900699static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
700 u32 reg_offset, const u8 *buf, u32 len)
701{
702 if ((reg_offset + len) > 32)
703 return -EINVAL;
704
705 if (hdata->hdmiphy_port) {
706 int ret;
707
708 ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
709 if (ret == len)
710 return 0;
711 return ret;
712 } else {
713 int i;
714 for (i = 0; i < len; i++)
Andrzej Hajda1993c332015-09-25 14:48:19 +0200715 writel(buf[i], hdata->regs_hdmiphy +
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900716 ((reg_offset + i)<<2));
717 return 0;
718 }
719}
720
Andrzej Hajda9be7e982016-01-14 14:22:47 +0900721static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
722{
723 int i, ret;
724
725 for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
726 ret = clk_prepare_enable(hdata->clk_gates[i]);
727 if (!ret)
728 continue;
729
730 dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
731 hdata->drv_data->clk_gates.data[i], ret);
732 while (i--)
733 clk_disable_unprepare(hdata->clk_gates[i]);
734 return ret;
735 }
736
737 return 0;
738}
739
740static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
741{
742 int i = hdata->drv_data->clk_gates.count;
743
744 while (i--)
745 clk_disable_unprepare(hdata->clk_gates[i]);
746}
747
748static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
749{
750 struct device *dev = hdata->dev;
751 int ret = 0;
752 int i;
753
754 for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
755 struct clk **c = &hdata->clk_muxes[i];
756
757 ret = clk_set_parent(c[2], c[to_phy]);
758 if (!ret)
759 continue;
760
761 dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
762 hdata->drv_data->clk_muxes.data[i + 2],
763 hdata->drv_data->clk_muxes.data[i + to_phy], ret);
764 }
765
766 return ret;
767}
768
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100769static void hdmi_reg_infoframes(struct hdmi_context *hdata)
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530770{
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100771 union hdmi_infoframe frm;
772 u8 buf[25];
773 int ret;
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530774
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530775 if (hdata->dvi_mode) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530776 hdmi_reg_writeb(hdata, HDMI_AVI_CON,
777 HDMI_AVI_CON_DO_NOT_TRANSMIT);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100778 hdmi_reg_writeb(hdata, HDMI_VSI_CON,
779 HDMI_VSI_CON_DO_NOT_TRANSMIT);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530780 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
781 return;
782 }
783
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100784 ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
785 &hdata->current_mode);
786 if (!ret)
787 ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
788 if (ret > 0) {
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530789 hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100790 hdmi_reg_write_buf(hdata, HDMI_AVI_HEADER0, buf, ret);
791 } else {
792 DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
793 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530794
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100795 ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
796 &hdata->current_mode);
797 if (!ret)
798 ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
799 sizeof(buf));
800 if (ret > 0) {
801 hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC);
Andrzej Hajda10abdbc2017-01-20 07:52:20 +0100802 hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3);
803 hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100804 }
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530805
Andrzej Hajda5f9e2282016-11-07 16:04:43 +0100806 ret = hdmi_audio_infoframe_init(&frm.audio);
807 if (!ret) {
808 frm.audio.channels = 2;
809 ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
810 }
811 if (ret > 0) {
812 hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
813 hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +0530814 }
815}
816
Sean Pauld9716ee2014-01-30 16:19:29 -0500817static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
818 bool force)
Sean Paul45517892014-01-30 16:19:05 -0500819{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200820 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Paul45517892014-01-30 16:19:05 -0500821
Andrzej Hajda2228b7c2015-09-25 14:48:24 +0200822 if (gpiod_get_value(hdata->hpd_gpio))
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200823 return connector_status_connected;
Sean Paul5137c8c2014-04-03 20:41:03 +0530824
Andrzej Hajdaef6ce282015-07-09 16:28:07 +0200825 return connector_status_disconnected;
Sean Paul45517892014-01-30 16:19:05 -0500826}
827
Sean Pauld9716ee2014-01-30 16:19:29 -0500828static void hdmi_connector_destroy(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900829{
Andrzej Hajdaad279312014-09-09 15:16:13 +0200830 drm_connector_unregister(connector);
831 drm_connector_cleanup(connector);
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900832}
833
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100834static const struct drm_connector_funcs hdmi_connector_funcs = {
Gustavo Padovan63498e32015-06-01 12:04:53 -0300835 .dpms = drm_atomic_helper_connector_dpms,
Sean Pauld9716ee2014-01-30 16:19:29 -0500836 .fill_modes = drm_helper_probe_single_connector_modes,
837 .detect = hdmi_detect,
838 .destroy = hdmi_connector_destroy,
Gustavo Padovan4ea95262015-06-01 12:04:44 -0300839 .reset = drm_atomic_helper_connector_reset,
840 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
841 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Sean Pauld9716ee2014-01-30 16:19:29 -0500842};
843
844static int hdmi_get_modes(struct drm_connector *connector)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900845{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200846 struct hdmi_context *hdata = connector_to_hdmi(connector);
Sean Pauld9716ee2014-01-30 16:19:29 -0500847 struct edid *edid;
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200848 int ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900849
Inki Dae8fa04aa2014-03-13 16:38:31 +0900850 if (!hdata->ddc_adpt)
Sean Pauld9716ee2014-01-30 16:19:29 -0500851 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900852
Inki Dae8fa04aa2014-03-13 16:38:31 +0900853 edid = drm_get_edid(connector, hdata->ddc_adpt);
Sean Pauld9716ee2014-01-30 16:19:29 -0500854 if (!edid)
855 return -ENODEV;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900856
Sean Pauld9716ee2014-01-30 16:19:29 -0500857 hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500858 DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
859 (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
Sean Pauld9716ee2014-01-30 16:19:29 -0500860 edid->width_cm, edid->height_cm);
Rahul Sharma9c08e4b2013-01-04 07:59:11 -0500861
Sean Pauld9716ee2014-01-30 16:19:29 -0500862 drm_mode_connector_update_edid_property(connector, edid);
863
Andrzej Hajda64ebd892015-07-09 08:25:38 +0200864 ret = drm_add_edid_modes(connector, edid);
865
866 kfree(edid);
867
868 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900869}
870
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900871static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900872{
Andrzej Hajda65e98032015-11-02 14:16:41 +0100873 const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
Rahul Sharmad5e9ca42014-05-09 15:34:18 +0900874 int i;
Seung-Woo Kimd8408322011-12-21 17:39:39 +0900875
Andrzej Hajda65e98032015-11-02 14:16:41 +0100876 for (i = 0; i < confs->count; i++)
877 if (confs->data[i].pixel_clock == pixel_clock)
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500878 return i;
Sean Paul2f7e2ed2013-01-15 08:11:08 -0500879
880 DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
881 return -EINVAL;
882}
883
Sean Pauld9716ee2014-01-30 16:19:29 -0500884static int hdmi_mode_valid(struct drm_connector *connector,
Sean Paulf041b252014-01-30 16:19:15 -0500885 struct drm_display_mode *mode)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900886{
Andrzej Hajda185f22d2015-09-25 14:48:26 +0200887 struct hdmi_context *hdata = connector_to_hdmi(connector);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900888 int ret;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900889
Rahul Sharma16844fb2013-06-10 14:50:00 +0530890 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
891 mode->hdisplay, mode->vdisplay, mode->vrefresh,
892 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
893 false, mode->clock * 1000);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +0900894
Rahul Sharma16844fb2013-06-10 14:50:00 +0530895 ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900896 if (ret < 0)
Sean Pauld9716ee2014-01-30 16:19:29 -0500897 return MODE_BAD;
898
899 return MODE_OK;
900}
901
Ville Syrjälä800ba2b2015-12-15 12:21:06 +0100902static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
Sean Pauld9716ee2014-01-30 16:19:29 -0500903 .get_modes = hdmi_get_modes,
904 .mode_valid = hdmi_mode_valid,
Sean Pauld9716ee2014-01-30 16:19:29 -0500905};
906
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300907static int hdmi_create_connector(struct drm_encoder *encoder)
Sean Pauld9716ee2014-01-30 16:19:29 -0500908{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300909 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500910 struct drm_connector *connector = &hdata->connector;
911 int ret;
912
Sean Pauld9716ee2014-01-30 16:19:29 -0500913 connector->interlace_allowed = true;
914 connector->polled = DRM_CONNECTOR_POLL_HPD;
915
916 ret = drm_connector_init(hdata->drm_dev, connector,
917 &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
918 if (ret) {
919 DRM_ERROR("Failed to initialize connector with drm\n");
Rahul Sharma6b986ed2013-03-06 17:33:29 +0900920 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500921 }
922
923 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
Thomas Wood34ea3d32014-05-29 16:57:41 +0100924 drm_connector_register(connector);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300925 drm_mode_connector_attach_encoder(connector, encoder);
Sean Pauld9716ee2014-01-30 16:19:29 -0500926
Andrzej Hajdaaa181572017-02-01 09:29:14 +0100927 if (hdata->bridge) {
928 encoder->bridge = hdata->bridge;
929 hdata->bridge->encoder = encoder;
930 ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
931 if (ret)
932 DRM_ERROR("Failed to attach bridge\n");
933 }
934
935 return ret;
Sean Pauld9716ee2014-01-30 16:19:29 -0500936}
937
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300938static bool hdmi_mode_fixup(struct drm_encoder *encoder,
939 const struct drm_display_mode *mode,
940 struct drm_display_mode *adjusted_mode)
Sean Paulf041b252014-01-30 16:19:15 -0500941{
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300942 struct drm_device *dev = encoder->dev;
943 struct drm_connector *connector;
Sean Paulf041b252014-01-30 16:19:15 -0500944 struct drm_display_mode *m;
945 int mode_ok;
946
Sean Paulf041b252014-01-30 16:19:15 -0500947 drm_mode_set_crtcinfo(adjusted_mode, 0);
948
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300949 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
950 if (connector->encoder == encoder)
951 break;
952 }
953
954 if (connector->encoder != encoder)
955 return true;
956
Sean Pauld9716ee2014-01-30 16:19:29 -0500957 mode_ok = hdmi_mode_valid(connector, adjusted_mode);
Sean Paulf041b252014-01-30 16:19:15 -0500958
Sean Pauld9716ee2014-01-30 16:19:29 -0500959 if (mode_ok == MODE_OK)
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300960 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500961
962 /*
Andrzej Hajda5eefadb2016-01-14 14:28:20 +0900963 * Find the most suitable mode and copy it to adjusted_mode.
Sean Paulf041b252014-01-30 16:19:15 -0500964 */
965 list_for_each_entry(m, &connector->modes, head) {
Sean Pauld9716ee2014-01-30 16:19:29 -0500966 mode_ok = hdmi_mode_valid(connector, m);
Sean Paulf041b252014-01-30 16:19:15 -0500967
Sean Pauld9716ee2014-01-30 16:19:29 -0500968 if (mode_ok == MODE_OK) {
Sean Paulf041b252014-01-30 16:19:15 -0500969 DRM_INFO("desired mode doesn't exist so\n");
970 DRM_INFO("use the most suitable mode among modes.\n");
971
972 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
973 m->hdisplay, m->vdisplay, m->vrefresh);
974
Sean Paul75626852014-01-30 16:19:16 -0500975 drm_mode_copy(adjusted_mode, m);
Sean Paulf041b252014-01-30 16:19:15 -0500976 break;
977 }
978 }
Gustavo Padovan2b8376c2015-08-15 12:14:08 -0300979
980 return true;
Sean Paulf041b252014-01-30 16:19:15 -0500981}
982
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200983static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900984{
985 u32 n, cts;
986
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200987 cts = (freq % 9) ? 27000 : 30000;
988 n = 128 * freq / (27000000 / cts);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900989
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +0200990 hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
991 hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
992 hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
Andrzej Hajda633d00b2015-09-25 14:48:16 +0200993 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900994}
995
996static void hdmi_audio_init(struct hdmi_context *hdata)
997{
Sachin Kamat7a9bf6e2014-07-02 09:33:07 +0530998 u32 sample_rate, bits_per_sample;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +0900999 u32 data_num, bit_ch, sample_frq;
1000 u32 val;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001001
1002 sample_rate = 44100;
1003 bits_per_sample = 16;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001004
1005 switch (bits_per_sample) {
1006 case 20:
1007 data_num = 2;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001008 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001009 break;
1010 case 24:
1011 data_num = 3;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001012 bit_ch = 1;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001013 break;
1014 default:
1015 data_num = 1;
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001016 bit_ch = 0;
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001017 break;
1018 }
1019
Andrzej Hajdad24bb3e2015-09-25 14:48:27 +02001020 hdmi_reg_acr(hdata, sample_rate);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001021
1022 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
1023 | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
1024 | HDMI_I2S_MUX_ENABLE);
1025
1026 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
1027 | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
1028
1029 hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
1030
1031 sample_frq = (sample_rate == 44100) ? 0 :
1032 (sample_rate == 48000) ? 2 :
1033 (sample_rate == 32000) ? 3 :
1034 (sample_rate == 96000) ? 0xa : 0x0;
1035
1036 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
1037 hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
1038
1039 val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
1040 hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
1041
1042 /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
1043 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
1044 | HDMI_I2S_SEL_LRCK(6));
1045 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
1046 | HDMI_I2S_SEL_SDATA2(4));
1047 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
1048 | HDMI_I2S_SEL_SDATA2(2));
1049 hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
1050
1051 /* I2S_CON_1 & 2 */
1052 hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
1053 | HDMI_I2S_L_CH_LOW_POL);
1054 hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
1055 | HDMI_I2S_SET_BIT_CH(bit_ch)
1056 | HDMI_I2S_SET_SDATA_BIT(data_num)
1057 | HDMI_I2S_BASIC_FORMAT);
1058
1059 /* Configure register related to CUV information */
1060 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
1061 | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
1062 | HDMI_I2S_COPYRIGHT
1063 | HDMI_I2S_LINEAR_PCM
1064 | HDMI_I2S_CONSUMER_FORMAT);
1065 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
1066 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
1067 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
1068 | HDMI_I2S_SET_SMP_FREQ(sample_frq));
1069 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
1070 HDMI_I2S_ORG_SMP_FREQ_44_1
1071 | HDMI_I2S_WORD_LEN_MAX24_24BITS
1072 | HDMI_I2S_WORD_LEN_MAX_24BITS);
1073
1074 hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
1075}
1076
1077static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
1078{
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001079 if (hdata->dvi_mode)
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001080 return;
1081
1082 hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
1083 hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
1084 HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
1085}
1086
Rahul Sharmabfa48422014-04-03 20:41:04 +05301087static void hdmi_start(struct hdmi_context *hdata, bool start)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001088{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301089 u32 val = start ? HDMI_TG_EN : 0;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001090
Rahul Sharmabfa48422014-04-03 20:41:04 +05301091 if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
1092 val |= HDMI_FIELD_EN;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001093
Rahul Sharmabfa48422014-04-03 20:41:04 +05301094 hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
1095 hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001096}
1097
1098static void hdmi_conf_init(struct hdmi_context *hdata)
1099{
Sean Paul77006a72013-01-16 10:17:20 -05001100 /* disable HPD interrupts from HDMI IP block, use GPIO instead */
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001101 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
1102 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001103
1104 /* choose HDMI mode */
1105 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1106 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001107 /* apply video pre-amble and guard band in HDMI mode only */
Shirish S9a8e1cb2014-02-14 13:04:57 +05301108 hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001109 /* disable bluescreen */
1110 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001111
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001112 if (hdata->dvi_mode) {
Seung-Woo Kim872d20d62012-04-24 17:39:15 +09001113 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
1114 HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
1115 hdmi_reg_writeb(hdata, HDMI_CON_2,
1116 HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
1117 }
1118
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001119 if (hdata->drv_data->type == HDMI_TYPE13) {
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001120 /* choose bluescreen (fecal) color */
1121 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
1122 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
1123 hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
1124
1125 /* enable AVI packet every vsync, fixes purple line problem */
1126 hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
1127 /* force RGB, look to CEA-861-D, table 7 for more detail */
1128 hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
1129 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
1130
1131 hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
1132 hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
1133 hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
1134 } else {
Andrzej Hajda5f9e2282016-11-07 16:04:43 +01001135 hdmi_reg_infoframes(hdata);
Rahul Sharmaa144c2e2012-11-26 10:52:57 +05301136
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001137 /* enable AVI packet every vsync, fixes purple line problem */
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001138 hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
1139 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001140}
1141
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001142static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
1143{
1144 int tries;
1145
1146 for (tries = 0; tries < 10; ++tries) {
1147 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
1148
1149 if (val & HDMI_PHY_STATUS_READY) {
1150 DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
1151 return;
1152 }
1153 usleep_range(10, 20);
1154 }
1155
1156 DRM_ERROR("PLL could not reach steady state\n");
1157}
1158
Rahul Sharma16844fb2013-06-10 14:50:00 +05301159static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001160{
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001161 struct drm_display_mode *m = &hdata->current_mode;
1162 unsigned int val;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001163
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001164 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1165 hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
1166 (m->htotal << 12) | m->vtotal);
1167
1168 val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
1169 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
1170
1171 val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
1172 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
1173
1174 val = (m->hsync_start - m->hdisplay - 2);
1175 val |= ((m->hsync_end - m->hdisplay - 2) << 10);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001176 val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001177 hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
1178
1179 /*
1180 * Quirk requirement for exynos HDMI IP design,
1181 * 2 pixels less than the actual calculation for hsync_start
1182 * and end.
1183 */
1184
1185 /* Following values & calculations differ for different type of modes */
1186 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001187 val = ((m->vsync_end - m->vdisplay) / 2);
1188 val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
1189 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1190
1191 val = m->vtotal / 2;
1192 val |= ((m->vtotal - m->vdisplay) / 2) << 11;
1193 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1194
1195 val = (m->vtotal +
1196 ((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
1197 val |= m->vtotal << 11;
1198 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
1199
1200 val = ((m->vtotal / 2) + 7);
1201 val |= ((m->vtotal / 2) + 2) << 12;
1202 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
1203
1204 val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
1205 val |= ((m->htotal / 2) +
1206 (m->hsync_start - m->hdisplay)) << 12;
1207 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
1208
1209 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1210 (m->vtotal - m->vdisplay) / 2);
1211 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1212
1213 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
1214 } else {
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001215 val = m->vtotal;
1216 val |= (m->vtotal - m->vdisplay) << 11;
1217 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
1218
1219 hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
1220
1221 val = (m->vsync_end - m->vdisplay);
1222 val |= ((m->vsync_start - m->vdisplay) << 12);
1223 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
1224
1225 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
1226 hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
1227 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1228 m->vtotal - m->vdisplay);
1229 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001230 }
1231
Andrzej Hajdaedb6e412015-07-09 16:28:11 +02001232 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1233 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1234 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1235 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001236}
1237
Rahul Sharma16844fb2013-06-10 14:50:00 +05301238static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001239{
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001240 struct drm_display_mode *m = &hdata->current_mode;
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001241
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001242 hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
1243 hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
1244 hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
1245 hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001246 (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001247 hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
1248 (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
1249 hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
1250 (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1251
1252 /*
1253 * Quirk requirement for exynos 5 HDMI IP design,
1254 * 2 pixels less than the actual calculation for hsync_start
1255 * and end.
1256 */
1257
1258 /* Following values & calculations differ for different type of modes */
1259 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001260 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1261 (m->vsync_end - m->vdisplay) / 2);
1262 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1263 (m->vsync_start - m->vdisplay) / 2);
1264 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
1265 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1266 (m->vtotal - m->vdisplay) / 2);
1267 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
1268 m->vtotal - m->vdisplay / 2);
1269 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
1270 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
1271 (m->vtotal / 2) + 7);
1272 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
1273 (m->vtotal / 2) + 2);
1274 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
1275 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1276 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
1277 (m->htotal / 2) + (m->hsync_start - m->hdisplay));
1278 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1279 (m->vtotal - m->vdisplay) / 2);
1280 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
1281 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
1282 m->vtotal - m->vdisplay / 2);
1283 hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
1284 (m->vtotal / 2) + 1);
1285 hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
1286 (m->vtotal / 2) + 1);
1287 hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
1288 (m->vtotal / 2) + 1);
1289 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
1290 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
1291 } else {
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001292 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
1293 m->vsync_end - m->vdisplay);
1294 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
1295 m->vsync_start - m->vdisplay);
1296 hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
1297 hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
1298 m->vtotal - m->vdisplay);
1299 hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
1300 hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
1301 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
1302 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
1303 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
1304 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
1305 hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
1306 m->vtotal - m->vdisplay);
1307 hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001308 }
1309
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001310 hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
1311 m->hsync_start - m->hdisplay - 2);
1312 hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
1313 m->hsync_end - m->hdisplay - 2);
1314 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
1315 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
1316 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
1317 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
1318 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
1319 hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
1320 hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
1321 hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
1322 hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
1323 hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
1324 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
1325 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
1326 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
1327 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
1328 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
1329 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
1330 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
1331 hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001332
Andrzej Hajda7b5102d2015-07-09 16:28:12 +02001333 hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
1334 hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
1335 hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
1336 hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001337 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1338 hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001339}
1340
Rahul Sharma16844fb2013-06-10 14:50:00 +05301341static void hdmi_mode_apply(struct hdmi_context *hdata)
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001342{
Andrzej Hajdacd240cd2015-07-09 16:28:09 +02001343 if (hdata->drv_data->type == HDMI_TYPE13)
Rahul Sharma16844fb2013-06-10 14:50:00 +05301344 hdmi_v13_mode_apply(hdata);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001345 else
Rahul Sharma16844fb2013-06-10 14:50:00 +05301346 hdmi_v14_mode_apply(hdata);
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001347
Andrzej Hajda8eb6d4e2015-09-25 14:48:17 +02001348 hdmi_start(hdata, true);
Joonyoung Shim3ecd70b2012-03-16 18:47:03 +09001349}
1350
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001351static void hdmiphy_conf_reset(struct hdmi_context *hdata)
1352{
Andrzej Hajda69f88872016-03-23 14:15:14 +01001353 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
1354 usleep_range(10000, 12000);
1355 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
1356 usleep_range(10000, 12000);
Andrzej Hajda633d00b2015-09-25 14:48:16 +02001357 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001358 usleep_range(10000, 12000);
Andrzej Hajda5eefadb2016-01-14 14:28:20 +09001359 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
Sean Paul09760ea2013-01-14 17:03:20 -05001360 usleep_range(10000, 12000);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001361}
1362
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001363static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
1364{
1365 u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
1366
1367 if (hdata->drv_data == &exynos5433_hdmi_driver_data)
1368 writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
1369}
1370
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001371static void hdmiphy_conf_apply(struct hdmi_context *hdata)
1372{
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001373 int ret;
Andrzej Hajda4677f512016-03-23 14:15:12 +01001374 const u8 *phy_conf;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001375
Andrzej Hajda4677f512016-03-23 14:15:12 +01001376 ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
1377 if (ret < 0) {
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001378 DRM_ERROR("failed to find hdmiphy conf\n");
1379 return;
1380 }
Andrzej Hajda4677f512016-03-23 14:15:12 +01001381 phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
1382
1383 hdmi_clk_set_parents(hdata, false);
1384
1385 hdmiphy_conf_reset(hdata);
Sean Paul2f7e2ed2013-01-15 08:11:08 -05001386
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001387 hdmiphy_enable_mode_set(hdata, true);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001388 ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
Rahul Sharmad5e9ca42014-05-09 15:34:18 +09001389 if (ret) {
1390 DRM_ERROR("failed to configure hdmiphy\n");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001391 return;
1392 }
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001393 hdmiphy_enable_mode_set(hdata, false);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001394 hdmi_clk_set_parents(hdata, true);
Sean Paul09760ea2013-01-14 17:03:20 -05001395 usleep_range(10000, 12000);
Andrzej Hajda4677f512016-03-23 14:15:12 +01001396 hdmiphy_wait_for_pll(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001397}
1398
1399static void hdmi_conf_apply(struct hdmi_context *hdata)
1400{
Rahul Sharmabfa48422014-04-03 20:41:04 +05301401 hdmi_start(hdata, false);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001402 hdmi_conf_init(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001403 hdmi_audio_init(hdata);
Rahul Sharma16844fb2013-06-10 14:50:00 +05301404 hdmi_mode_apply(hdata);
Seung-Woo Kim3e148ba2012-03-16 18:47:16 +09001405 hdmi_audio_control(hdata, true);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001406}
1407
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001408static void hdmi_mode_set(struct drm_encoder *encoder,
1409 struct drm_display_mode *mode,
1410 struct drm_display_mode *adjusted_mode)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001411{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001412 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001413 struct drm_display_mode *m = adjusted_mode;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001414
YoungJun Chocbc4c332013-06-12 10:44:40 +09001415 DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
1416 m->hdisplay, m->vdisplay,
Rahul Sharma6b986ed2013-03-06 17:33:29 +09001417 m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
Tobias Jakobi1e6d4592015-04-07 01:14:50 +02001418 "INTERLACED" : "PROGRESSIVE");
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001419
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001420 drm_mode_copy(&hdata->current_mode, m);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001421}
1422
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001423static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
1424{
1425 if (!hdata->sysreg)
1426 return;
1427
1428 regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
1429 SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
1430}
1431
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001432static void hdmiphy_enable(struct hdmi_context *hdata)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001433{
Andrzej Hajda882a0642015-07-09 16:28:08 +02001434 if (hdata->powered)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001435 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001436
Sean Paulaf65c802014-01-30 16:19:27 -05001437 pm_runtime_get_sync(hdata->dev);
1438
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001439 if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
Seung-Woo Kimad079452013-06-05 14:34:38 +09001440 DRM_DEBUG_KMS("failed to enable regulator bulk\n");
1441
Rahul Sharma049d34e2014-05-20 10:36:05 +05301442 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1443 PMU_HDMI_PHY_ENABLE_BIT, 1);
1444
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001445 hdmi_set_refclk(hdata, true);
1446
Andrzej Hajda5dd45e22016-03-23 14:15:13 +01001447 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
1448
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001449 hdmiphy_conf_apply(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001450
1451 hdata->powered = true;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001452}
1453
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001454static void hdmiphy_disable(struct hdmi_context *hdata)
1455{
1456 if (!hdata->powered)
1457 return;
1458
1459 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
1460
1461 hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
1462
1463 hdmi_set_refclk(hdata, false);
1464
1465 regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
1466 PMU_HDMI_PHY_ENABLE_BIT, 0);
1467
1468 regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
1469
1470 pm_runtime_put_sync(hdata->dev);
1471
1472 hdata->powered = false;
1473}
1474
1475static void hdmi_enable(struct drm_encoder *encoder)
1476{
1477 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
1478
1479 hdmiphy_enable(hdata);
1480 hdmi_conf_apply(hdata);
1481}
1482
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001483static void hdmi_disable(struct drm_encoder *encoder)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001484{
Gustavo Padovancf67cc92015-08-11 17:38:06 +09001485 struct hdmi_context *hdata = encoder_to_hdmi(encoder);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001486 struct drm_crtc *crtc = encoder->crtc;
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001487 const struct drm_crtc_helper_funcs *funcs = NULL;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001488
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001489 if (!hdata->powered)
Andrzej Hajda882a0642015-07-09 16:28:08 +02001490 return;
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001491
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001492 /*
1493 * The SFRs of VP and Mixer are updated by Vertical Sync of
1494 * Timing generator which is a part of HDMI so the sequence
1495 * to disable TV Subsystem should be as following,
1496 * VP -> Mixer -> HDMI
1497 *
1498 * Below codes will try to disable Mixer and VP(if used)
1499 * prior to disabling HDMI.
1500 */
1501 if (crtc)
1502 funcs = crtc->helper_private;
1503 if (funcs && funcs->disable)
1504 (*funcs->disable)(crtc);
1505
Sean Paul724fd142014-05-09 15:05:10 +09001506 cancel_delayed_work(&hdata->hotplug_work);
1507
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001508 hdmiphy_disable(hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001509}
1510
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001511static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
Sean Paulf041b252014-01-30 16:19:15 -05001512 .mode_fixup = hdmi_mode_fixup,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001513 .mode_set = hdmi_mode_set,
Gustavo Padovanb6595dc2015-08-10 21:37:04 -03001514 .enable = hdmi_enable,
1515 .disable = hdmi_disable,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001516};
1517
Ville Syrjälä800ba2b2015-12-15 12:21:06 +01001518static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001519 .destroy = drm_encoder_cleanup,
1520};
1521
Sean Paul724fd142014-05-09 15:05:10 +09001522static void hdmi_hotplug_work_func(struct work_struct *work)
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001523{
Sean Paul724fd142014-05-09 15:05:10 +09001524 struct hdmi_context *hdata;
1525
1526 hdata = container_of(work, struct hdmi_context, hotplug_work.work);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001527
Sean Paul45517892014-01-30 16:19:05 -05001528 if (hdata->drm_dev)
1529 drm_helper_hpd_irq_event(hdata->drm_dev);
Sean Paul724fd142014-05-09 15:05:10 +09001530}
1531
1532static irqreturn_t hdmi_irq_thread(int irq, void *arg)
1533{
1534 struct hdmi_context *hdata = arg;
1535
1536 mod_delayed_work(system_wq, &hdata->hotplug_work,
1537 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001538
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001539 return IRQ_HANDLED;
1540}
1541
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001542static int hdmi_clks_get(struct hdmi_context *hdata,
1543 const struct string_array_spec *names,
1544 struct clk **clks)
1545{
1546 struct device *dev = hdata->dev;
1547 int i;
1548
1549 for (i = 0; i < names->count; ++i) {
1550 struct clk *clk = devm_clk_get(dev, names->data[i]);
1551
1552 if (IS_ERR(clk)) {
1553 int ret = PTR_ERR(clk);
1554
1555 dev_err(dev, "Cannot get clock %s, %d\n",
1556 names->data[i], ret);
1557
1558 return ret;
1559 }
1560
1561 clks[i] = clk;
1562 }
1563
1564 return 0;
1565}
1566
1567static int hdmi_clk_init(struct hdmi_context *hdata)
1568{
1569 const struct hdmi_driver_data *drv_data = hdata->drv_data;
1570 int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
1571 struct device *dev = hdata->dev;
1572 struct clk **clks;
1573 int ret;
1574
1575 if (!count)
1576 return 0;
1577
1578 clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
1579 if (!clks)
Dan Carpenterf9628c22016-05-12 22:54:57 +03001580 return -ENOMEM;
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001581
1582 hdata->clk_gates = clks;
1583 hdata->clk_muxes = clks + drv_data->clk_gates.count;
1584
1585 ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
1586 if (ret)
1587 return ret;
1588
1589 return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
1590}
1591
1592
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001593static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
1594{
1595 struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
1596 phy_clk);
1597
1598 if (enable)
1599 hdmiphy_enable(hdata);
1600 else
1601 hdmiphy_disable(hdata);
1602}
1603
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001604static int hdmi_bridge_init(struct hdmi_context *hdata)
1605{
1606 struct device *dev = hdata->dev;
1607 struct device_node *ep, *np;
1608
1609 ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
1610 if (!ep)
1611 return 0;
1612
1613 np = of_graph_get_remote_port_parent(ep);
1614 of_node_put(ep);
1615 if (!np) {
1616 DRM_ERROR("failed to get remote port parent");
1617 return -EINVAL;
1618 }
1619
1620 hdata->bridge = of_drm_find_bridge(np);
1621 of_node_put(np);
1622
1623 if (!hdata->bridge)
1624 return -EPROBE_DEFER;
1625
1626 return 0;
1627}
1628
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001629static int hdmi_resources_init(struct hdmi_context *hdata)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001630{
1631 struct device *dev = hdata->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001632 int i, ret;
1633
1634 DRM_DEBUG_KMS("HDMI resource init\n");
1635
Andrzej Hajda2228b7c2015-09-25 14:48:24 +02001636 hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
1637 if (IS_ERR(hdata->hpd_gpio)) {
1638 DRM_ERROR("cannot get hpd gpio property\n");
1639 return PTR_ERR(hdata->hpd_gpio);
1640 }
1641
1642 hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
1643 if (hdata->irq < 0) {
1644 DRM_ERROR("failed to get GPIO irq\n");
1645 return hdata->irq;
1646 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001647
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001648 ret = hdmi_clk_init(hdata);
1649 if (ret)
1650 return ret;
1651
1652 ret = hdmi_clk_set_parents(hdata, false);
1653 if (ret)
1654 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001655
Milo Kimc0d656d2016-08-31 15:14:27 +09001656 for (i = 0; i < ARRAY_SIZE(supply); ++i)
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001657 hdata->regul_bulk[i].supply = supply[i];
Milo Kimc0d656d2016-08-31 15:14:27 +09001658
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001659 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001660 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001661 if (ret != -EPROBE_DEFER)
1662 DRM_ERROR("failed to get regulators\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001663 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001664 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001665
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001666 hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001667
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001668 if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) {
1669 if (IS_ERR(hdata->reg_hdmi_en))
1670 return PTR_ERR(hdata->reg_hdmi_en);
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001671
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001672 ret = regulator_enable(hdata->reg_hdmi_en);
1673 if (ret) {
1674 DRM_ERROR("failed to enable hdmi-en regulator\n");
1675 return ret;
1676 }
1677 }
Andrzej Hajda498d5a32015-09-25 14:48:21 +02001678
Andrzej Hajdaaa181572017-02-01 09:29:14 +01001679 return hdmi_bridge_init(hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001680}
1681
Rahul Sharma22c4f422012-10-04 20:48:55 +05301682static struct of_device_id hdmi_match_types[] = {
1683 {
Marek Szyprowskiff830c92014-07-01 10:10:07 +02001684 .compatible = "samsung,exynos4210-hdmi",
1685 .data = &exynos4210_hdmi_driver_data,
1686 }, {
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301687 .compatible = "samsung,exynos4212-hdmi",
Inki Daebfe4e842014-03-06 14:18:17 +09001688 .data = &exynos4212_hdmi_driver_data,
Rahul Sharmacc57caf2013-06-19 18:21:07 +05301689 }, {
Rahul Sharmaa18a2dd2014-04-20 15:51:17 +05301690 .compatible = "samsung,exynos5420-hdmi",
1691 .data = &exynos5420_hdmi_driver_data,
1692 }, {
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001693 .compatible = "samsung,exynos5433-hdmi",
1694 .data = &exynos5433_hdmi_driver_data,
1695 }, {
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301696 /* end node */
1697 }
1698};
Sjoerd Simons39b58a32014-07-18 22:36:41 +02001699MODULE_DEVICE_TABLE (of, hdmi_match_types);
Tomasz Stanislawskic119ed02012-10-04 20:48:44 +05301700
Inki Daef37cd5e2014-05-09 14:25:20 +09001701static int hdmi_bind(struct device *dev, struct device *master, void *data)
1702{
1703 struct drm_device *drm_dev = data;
Andrzej Hajda930865f2014-11-17 09:54:20 +01001704 struct hdmi_context *hdata = dev_get_drvdata(dev);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001705 struct drm_encoder *encoder = &hdata->encoder;
1706 int ret, pipe;
Inki Daef37cd5e2014-05-09 14:25:20 +09001707
Inki Daef37cd5e2014-05-09 14:25:20 +09001708 hdata->drm_dev = drm_dev;
1709
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001710 pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
1711 EXYNOS_DISPLAY_TYPE_HDMI);
1712 if (pipe < 0)
1713 return pipe;
Gustavo Padovana2986e82015-08-05 20:24:20 -03001714
Andrzej Hajda59b62d32016-05-10 13:56:32 +09001715 hdata->phy_clk.enable = hdmiphy_clk_enable;
1716
1717 exynos_drm_crtc_from_pipe(drm_dev, pipe)->pipe_clk = &hdata->phy_clk;
1718
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001719 encoder->possible_crtcs = 1 << pipe;
1720
1721 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
1722
1723 drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001724 DRM_MODE_ENCODER_TMDS, NULL);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001725
1726 drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
1727
1728 ret = hdmi_create_connector(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001729 if (ret) {
1730 DRM_ERROR("failed to create connector ret = %d\n", ret);
Gustavo Padovan2b8376c2015-08-15 12:14:08 -03001731 drm_encoder_cleanup(encoder);
Gustavo Padovana2986e82015-08-05 20:24:20 -03001732 return ret;
1733 }
1734
1735 return 0;
Inki Daef37cd5e2014-05-09 14:25:20 +09001736}
1737
1738static void hdmi_unbind(struct device *dev, struct device *master, void *data)
1739{
Inki Daef37cd5e2014-05-09 14:25:20 +09001740}
1741
1742static const struct component_ops hdmi_component_ops = {
1743 .bind = hdmi_bind,
1744 .unbind = hdmi_unbind,
1745};
1746
Milo Kim1caa3602016-08-31 15:14:25 +09001747static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001748{
1749 const char *compatible_str = "samsung,exynos4210-hdmiddc";
1750 struct device_node *np;
Milo Kim1caa3602016-08-31 15:14:25 +09001751 struct i2c_adapter *adpt;
Inki Daee2a562d2014-05-09 16:46:10 +09001752
1753 np = of_find_compatible_node(NULL, NULL, compatible_str);
1754 if (np)
Milo Kim1caa3602016-08-31 15:14:25 +09001755 np = of_get_next_parent(np);
1756 else
1757 np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
Inki Daee2a562d2014-05-09 16:46:10 +09001758
Milo Kim1caa3602016-08-31 15:14:25 +09001759 if (!np) {
1760 DRM_ERROR("Failed to find ddc node in device tree\n");
1761 return -ENODEV;
1762 }
1763
1764 adpt = of_find_i2c_adapter_by_node(np);
1765 of_node_put(np);
1766
1767 if (!adpt) {
1768 DRM_INFO("Failed to get ddc i2c adapter by node\n");
1769 return -EPROBE_DEFER;
1770 }
1771
1772 hdata->ddc_adpt = adpt;
1773
1774 return 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001775}
1776
Milo Kimb5413022016-08-31 15:14:26 +09001777static int hdmi_get_phy_io(struct hdmi_context *hdata)
Inki Daee2a562d2014-05-09 16:46:10 +09001778{
1779 const char *compatible_str = "samsung,exynos4212-hdmiphy";
Milo Kimb5413022016-08-31 15:14:26 +09001780 struct device_node *np;
1781 int ret = 0;
Inki Daee2a562d2014-05-09 16:46:10 +09001782
Milo Kimb5413022016-08-31 15:14:26 +09001783 np = of_find_compatible_node(NULL, NULL, compatible_str);
1784 if (!np) {
1785 np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
1786 if (!np) {
1787 DRM_ERROR("Failed to find hdmiphy node in device tree\n");
1788 return -ENODEV;
1789 }
1790 }
1791
1792 if (hdata->drv_data->is_apb_phy) {
1793 hdata->regs_hdmiphy = of_iomap(np, 0);
1794 if (!hdata->regs_hdmiphy) {
1795 DRM_ERROR("failed to ioremap hdmi phy\n");
1796 ret = -ENOMEM;
1797 goto out;
1798 }
1799 } else {
1800 hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
1801 if (!hdata->hdmiphy_port) {
1802 DRM_INFO("Failed to get hdmi phy i2c client\n");
1803 ret = -EPROBE_DEFER;
1804 goto out;
1805 }
1806 }
1807
1808out:
1809 of_node_put(np);
1810 return ret;
Inki Daee2a562d2014-05-09 16:46:10 +09001811}
1812
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001813static int hdmi_probe(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001814{
1815 struct device *dev = &pdev->dev;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001816 struct hdmi_context *hdata;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001817 struct resource *res;
1818 int ret;
1819
Andrzej Hajda930865f2014-11-17 09:54:20 +01001820 hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
1821 if (!hdata)
1822 return -ENOMEM;
1823
Marek Szyprowski57a64122016-04-01 15:17:44 +02001824 hdata->drv_data = of_device_get_match_data(dev);
Andrzej Hajda930865f2014-11-17 09:54:20 +01001825
Andrzej Hajda930865f2014-11-17 09:54:20 +01001826 platform_set_drvdata(pdev, hdata);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001827
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001828 hdata->dev = dev;
1829
1830 ret = hdmi_resources_init(hdata);
1831 if (ret) {
Javier Martinez Canillasb85881d2016-04-21 14:51:38 -04001832 if (ret != -EPROBE_DEFER)
1833 DRM_ERROR("hdmi_resources_init failed\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001834 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001835 }
1836
1837 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001838 hdata->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001839 if (IS_ERR(hdata->regs)) {
1840 ret = PTR_ERR(hdata->regs);
Andrzej Hajda86650402015-06-11 23:23:37 +09001841 return ret;
Inki Daedf5225b2014-05-29 18:28:02 +09001842 }
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001843
Milo Kim1caa3602016-08-31 15:14:25 +09001844 ret = hdmi_get_ddc_adapter(hdata);
1845 if (ret)
1846 return ret;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001847
Milo Kimb5413022016-08-31 15:14:26 +09001848 ret = hdmi_get_phy_io(hdata);
1849 if (ret)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001850 goto err_ddc;
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001851
Sean Paul724fd142014-05-09 15:05:10 +09001852 INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
1853
Seung-Woo Kimdcb9a7c2013-05-22 21:14:17 +09001854 ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
Sean Paul77006a72013-01-16 10:17:20 -05001855 hdmi_irq_thread, IRQF_TRIGGER_RISING |
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001856 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
Sean Paulf041b252014-01-30 16:19:15 -05001857 "hdmi", hdata);
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001858 if (ret) {
Sean Paul77006a72013-01-16 10:17:20 -05001859 DRM_ERROR("failed to register hdmi interrupt\n");
Joonyoung Shimcf8fc4f2012-04-23 19:35:50 +09001860 goto err_hdmiphy;
1861 }
1862
Rahul Sharma049d34e2014-05-20 10:36:05 +05301863 hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1864 "samsung,syscon-phandle");
1865 if (IS_ERR(hdata->pmureg)) {
1866 DRM_ERROR("syscon regmap lookup failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001867 ret = -EPROBE_DEFER;
Rahul Sharma049d34e2014-05-20 10:36:05 +05301868 goto err_hdmiphy;
1869 }
1870
Andrzej Hajda68cd0042016-01-14 14:40:07 +09001871 if (hdata->drv_data->has_sysreg) {
1872 hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1873 "samsung,sysreg-phandle");
1874 if (IS_ERR(hdata->sysreg)) {
1875 DRM_ERROR("sysreg regmap lookup failed.\n");
1876 ret = -EPROBE_DEFER;
1877 goto err_hdmiphy;
1878 }
1879 }
1880
Sean Paulaf65c802014-01-30 16:19:27 -05001881 pm_runtime_enable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001882
Inki Daedf5225b2014-05-29 18:28:02 +09001883 ret = component_add(&pdev->dev, &hdmi_component_ops);
1884 if (ret)
1885 goto err_disable_pm_runtime;
1886
1887 return ret;
1888
1889err_disable_pm_runtime:
1890 pm_runtime_disable(dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001891
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001892err_hdmiphy:
Paul Taysomb21a3bf2014-05-09 15:06:28 +09001893 if (hdata->hdmiphy_port)
1894 put_device(&hdata->hdmiphy_port->dev);
Arvind Yadavd7420002016-10-19 15:34:16 +05301895 if (hdata->regs_hdmiphy)
1896 iounmap(hdata->regs_hdmiphy);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001897err_ddc:
Inki Dae8fa04aa2014-03-13 16:38:31 +09001898 put_device(&hdata->ddc_adpt->dev);
Inki Daedf5225b2014-05-29 18:28:02 +09001899
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001900 return ret;
1901}
1902
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001903static int hdmi_remove(struct platform_device *pdev)
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001904{
Andrzej Hajda930865f2014-11-17 09:54:20 +01001905 struct hdmi_context *hdata = platform_get_drvdata(pdev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001906
Sean Paul724fd142014-05-09 15:05:10 +09001907 cancel_delayed_work_sync(&hdata->hotplug_work);
1908
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001909 component_del(&pdev->dev, &hdmi_component_ops);
1910
1911 pm_runtime_disable(&pdev->dev);
1912
Andrzej Hajdaaf1f7c22015-09-25 14:48:25 +02001913 if (!IS_ERR(hdata->reg_hdmi_en))
1914 regulator_disable(hdata->reg_hdmi_en);
Marek Szyprowski05fdf982014-07-01 10:10:06 +02001915
Seung-Woo Kim9d1e25c2014-07-28 17:15:22 +09001916 if (hdata->hdmiphy_port)
1917 put_device(&hdata->hdmiphy_port->dev);
Inki Daef37cd5e2014-05-09 14:25:20 +09001918
Arvind Yadavd7420002016-10-19 15:34:16 +05301919 if (hdata->regs_hdmiphy)
1920 iounmap(hdata->regs_hdmiphy);
1921
Andrzej Hajda2445c4a2015-09-25 14:48:20 +02001922 put_device(&hdata->ddc_adpt->dev);
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001923
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001924 return 0;
1925}
1926
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001927#ifdef CONFIG_PM
1928static int exynos_hdmi_suspend(struct device *dev)
1929{
1930 struct hdmi_context *hdata = dev_get_drvdata(dev);
1931
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001932 hdmi_clk_disable_gates(hdata);
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001933
1934 return 0;
1935}
1936
1937static int exynos_hdmi_resume(struct device *dev)
1938{
1939 struct hdmi_context *hdata = dev_get_drvdata(dev);
1940 int ret;
1941
Andrzej Hajda9be7e982016-01-14 14:22:47 +09001942 ret = hdmi_clk_enable_gates(hdata);
1943 if (ret < 0)
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001944 return ret;
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001945
1946 return 0;
1947}
1948#endif
1949
1950static const struct dev_pm_ops exynos_hdmi_pm_ops = {
1951 SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
1952};
1953
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001954struct platform_driver hdmi_driver = {
1955 .probe = hdmi_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001956 .remove = hdmi_remove,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001957 .driver = {
Rahul Sharma22c4f422012-10-04 20:48:55 +05301958 .name = "exynos-hdmi",
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001959 .owner = THIS_MODULE,
Gustavo Padovanf28464c2015-11-02 20:39:18 +09001960 .pm = &exynos_hdmi_pm_ops,
Sachin Kamat88c49812013-08-28 10:47:57 +05301961 .of_match_table = hdmi_match_types,
Seung-Woo Kimd8408322011-12-21 17:39:39 +09001962 },
1963};