raid5-ppl: PPL support for disks with write-back cache enabled
[linux-2.6-block.git] / drivers / gpu / drm / sun4i / sun4i_hdmi_enc.c
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  *
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  */
11
12 #include <drm/drmP.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_crtc_helper.h>
15 #include <drm/drm_edid.h>
16 #include <drm/drm_encoder.h>
17 #include <drm/drm_of.h>
18 #include <drm/drm_panel.h>
19
20 #include <linux/clk.h>
21 #include <linux/component.h>
22 #include <linux/iopoll.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/pm_runtime.h>
26 #include <linux/regmap.h>
27 #include <linux/reset.h>
28
29 #include "sun4i_backend.h"
30 #include "sun4i_crtc.h"
31 #include "sun4i_drv.h"
32 #include "sun4i_hdmi.h"
33
34 static inline struct sun4i_hdmi *
35 drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
36 {
37         return container_of(encoder, struct sun4i_hdmi,
38                             encoder);
39 }
40
41 static inline struct sun4i_hdmi *
42 drm_connector_to_sun4i_hdmi(struct drm_connector *connector)
43 {
44         return container_of(connector, struct sun4i_hdmi,
45                             connector);
46 }
47
48 static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
49                                            struct drm_display_mode *mode)
50 {
51         struct hdmi_avi_infoframe frame;
52         u8 buffer[17];
53         int i, ret;
54
55         ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, false);
56         if (ret < 0) {
57                 DRM_ERROR("Failed to get infoframes from mode\n");
58                 return ret;
59         }
60
61         ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
62         if (ret < 0) {
63                 DRM_ERROR("Failed to pack infoframes\n");
64                 return ret;
65         }
66
67         for (i = 0; i < sizeof(buffer); i++)
68                 writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i));
69
70         return 0;
71 }
72
73 static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
74                                    struct drm_crtc_state *crtc_state,
75                                    struct drm_connector_state *conn_state)
76 {
77         struct drm_display_mode *mode = &crtc_state->mode;
78
79         if (mode->flags & DRM_MODE_FLAG_DBLCLK)
80                 return -EINVAL;
81
82         return 0;
83 }
84
85 static void sun4i_hdmi_disable(struct drm_encoder *encoder)
86 {
87         struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
88         u32 val;
89
90         DRM_DEBUG_DRIVER("Disabling the HDMI Output\n");
91
92         val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
93         val &= ~SUN4I_HDMI_VID_CTRL_ENABLE;
94         writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
95 }
96
97 static void sun4i_hdmi_enable(struct drm_encoder *encoder)
98 {
99         struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
100         struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
101         u32 val = 0;
102
103         DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
104
105         sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
106         val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
107         val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
108         writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
109
110         val = SUN4I_HDMI_VID_CTRL_ENABLE;
111         if (hdmi->hdmi_monitor)
112                 val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
113
114         writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
115 }
116
117 static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
118                                 struct drm_display_mode *mode,
119                                 struct drm_display_mode *adjusted_mode)
120 {
121         struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
122         unsigned int x, y;
123         u32 val;
124
125         clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
126         clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
127
128         /* Set input sync enable */
129         writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC,
130                hdmi->base + SUN4I_HDMI_UNKNOWN_REG);
131
132         /*
133          * Setup output pad (?) controls
134          *
135          * This is done here instead of at probe/bind time because
136          * the controller seems to toggle some of the bits on its own.
137          *
138          * We can't just initialize the register there, we need to
139          * protect the clock bits that have already been read out and
140          * cached by the clock framework.
141          */
142         val = readl(hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
143         val &= SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;
144         val |= hdmi->variant->pad_ctrl1_init_val;
145         writel(val, hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
146         val = readl(hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
147
148         /* Setup timing registers */
149         writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay) |
150                SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay),
151                hdmi->base + SUN4I_HDMI_VID_TIMING_ACT_REG);
152
153         x = mode->htotal - mode->hsync_start;
154         y = mode->vtotal - mode->vsync_start;
155         writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
156                hdmi->base + SUN4I_HDMI_VID_TIMING_BP_REG);
157
158         x = mode->hsync_start - mode->hdisplay;
159         y = mode->vsync_start - mode->vdisplay;
160         writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
161                hdmi->base + SUN4I_HDMI_VID_TIMING_FP_REG);
162
163         x = mode->hsync_end - mode->hsync_start;
164         y = mode->vsync_end - mode->vsync_start;
165         writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
166                hdmi->base + SUN4I_HDMI_VID_TIMING_SPW_REG);
167
168         val = SUN4I_HDMI_VID_TIMING_POL_TX_CLK;
169         if (mode->flags & DRM_MODE_FLAG_PHSYNC)
170                 val |= SUN4I_HDMI_VID_TIMING_POL_HSYNC;
171
172         if (mode->flags & DRM_MODE_FLAG_PVSYNC)
173                 val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
174
175         writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
176 }
177
178 static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
179         .atomic_check   = sun4i_hdmi_atomic_check,
180         .disable        = sun4i_hdmi_disable,
181         .enable         = sun4i_hdmi_enable,
182         .mode_set       = sun4i_hdmi_mode_set,
183 };
184
185 static const struct drm_encoder_funcs sun4i_hdmi_funcs = {
186         .destroy        = drm_encoder_cleanup,
187 };
188
189 static int sun4i_hdmi_get_modes(struct drm_connector *connector)
190 {
191         struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
192         struct edid *edid;
193         int ret;
194
195         edid = drm_get_edid(connector, hdmi->i2c);
196         if (!edid)
197                 return 0;
198
199         hdmi->hdmi_monitor = drm_detect_hdmi_monitor(edid);
200         DRM_DEBUG_DRIVER("Monitor is %s monitor\n",
201                          hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
202
203         drm_mode_connector_update_edid_property(connector, edid);
204         cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
205         ret = drm_add_edid_modes(connector, edid);
206         kfree(edid);
207
208         return ret;
209 }
210
211 static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
212         .get_modes      = sun4i_hdmi_get_modes,
213 };
214
215 static enum drm_connector_status
216 sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
217 {
218         struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
219         unsigned long reg;
220
221         if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
222                                reg & SUN4I_HDMI_HPD_HIGH,
223                                0, 500000)) {
224                 cec_phys_addr_invalidate(hdmi->cec_adap);
225                 return connector_status_disconnected;
226         }
227
228         return connector_status_connected;
229 }
230
231 static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
232         .detect                 = sun4i_hdmi_connector_detect,
233         .fill_modes             = drm_helper_probe_single_connector_modes,
234         .destroy                = drm_connector_cleanup,
235         .reset                  = drm_atomic_helper_connector_reset,
236         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
237         .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
238 };
239
240 #ifdef CONFIG_DRM_SUN4I_HDMI_CEC
241 static bool sun4i_hdmi_cec_pin_read(struct cec_adapter *adap)
242 {
243         struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
244
245         return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX;
246 }
247
248 static void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap)
249 {
250         struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
251
252         /* Start driving the CEC pin low */
253         writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC);
254 }
255
256 static void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap)
257 {
258         struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
259
260         /*
261          * Stop driving the CEC pin, the pull up will take over
262          * unless another CEC device is driving the pin low.
263          */
264         writel(0, hdmi->base + SUN4I_HDMI_CEC);
265 }
266
267 static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = {
268         .read = sun4i_hdmi_cec_pin_read,
269         .low = sun4i_hdmi_cec_pin_low,
270         .high = sun4i_hdmi_cec_pin_high,
271 };
272 #endif
273
274 #define SUN4I_HDMI_PAD_CTRL1_MASK       (GENMASK(24, 7) | GENMASK(5, 0))
275 #define SUN4I_HDMI_PLL_CTRL_MASK        (GENMASK(31, 8) | GENMASK(3, 0))
276
277 /* Only difference from sun5i is AMP is 4 instead of 6 */
278 static const struct sun4i_hdmi_variant sun4i_variant = {
279         .pad_ctrl0_init_val     = SUN4I_HDMI_PAD_CTRL0_TXEN |
280                                   SUN4I_HDMI_PAD_CTRL0_CKEN |
281                                   SUN4I_HDMI_PAD_CTRL0_PWENG |
282                                   SUN4I_HDMI_PAD_CTRL0_PWEND |
283                                   SUN4I_HDMI_PAD_CTRL0_PWENC |
284                                   SUN4I_HDMI_PAD_CTRL0_LDODEN |
285                                   SUN4I_HDMI_PAD_CTRL0_LDOCEN |
286                                   SUN4I_HDMI_PAD_CTRL0_BIASEN,
287         .pad_ctrl1_init_val     = SUN4I_HDMI_PAD_CTRL1_REG_AMP(4) |
288                                   SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) |
289                                   SUN4I_HDMI_PAD_CTRL1_REG_DENCK |
290                                   SUN4I_HDMI_PAD_CTRL1_REG_DEN |
291                                   SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT |
292                                   SUN4I_HDMI_PAD_CTRL1_EMP_OPT |
293                                   SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT |
294                                   SUN4I_HDMI_PAD_CTRL1_AMP_OPT,
295         .pll_ctrl_init_val      = SUN4I_HDMI_PLL_CTRL_VCO_S(8) |
296                                   SUN4I_HDMI_PLL_CTRL_CS(7) |
297                                   SUN4I_HDMI_PLL_CTRL_CP_S(15) |
298                                   SUN4I_HDMI_PLL_CTRL_S(7) |
299                                   SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) |
300                                   SUN4I_HDMI_PLL_CTRL_SDIV2 |
301                                   SUN4I_HDMI_PLL_CTRL_LDO2_EN |
302                                   SUN4I_HDMI_PLL_CTRL_LDO1_EN |
303                                   SUN4I_HDMI_PLL_CTRL_HV_IS_33 |
304                                   SUN4I_HDMI_PLL_CTRL_BWS |
305                                   SUN4I_HDMI_PLL_CTRL_PLL_EN,
306
307         .ddc_clk_reg            = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6),
308         .ddc_clk_pre_divider    = 2,
309         .ddc_clk_m_offset       = 1,
310
311         .field_ddc_en           = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31),
312         .field_ddc_start        = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30),
313         .field_ddc_reset        = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0),
314         .field_ddc_addr_reg     = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31),
315         .field_ddc_slave_addr   = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6),
316         .field_ddc_int_status   = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8),
317         .field_ddc_fifo_clear   = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31),
318         .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7),
319         .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3),
320         .field_ddc_byte_count   = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9),
321         .field_ddc_cmd          = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2),
322         .field_ddc_sda_en       = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9),
323         .field_ddc_sck_en       = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8),
324
325         .ddc_fifo_reg           = SUN4I_HDMI_DDC_FIFO_DATA_REG,
326         .ddc_fifo_has_dir       = true,
327 };
328
329 static const struct sun4i_hdmi_variant sun5i_variant = {
330         .pad_ctrl0_init_val     = SUN4I_HDMI_PAD_CTRL0_TXEN |
331                                   SUN4I_HDMI_PAD_CTRL0_CKEN |
332                                   SUN4I_HDMI_PAD_CTRL0_PWENG |
333                                   SUN4I_HDMI_PAD_CTRL0_PWEND |
334                                   SUN4I_HDMI_PAD_CTRL0_PWENC |
335                                   SUN4I_HDMI_PAD_CTRL0_LDODEN |
336                                   SUN4I_HDMI_PAD_CTRL0_LDOCEN |
337                                   SUN4I_HDMI_PAD_CTRL0_BIASEN,
338         .pad_ctrl1_init_val     = SUN4I_HDMI_PAD_CTRL1_REG_AMP(6) |
339                                   SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) |
340                                   SUN4I_HDMI_PAD_CTRL1_REG_DENCK |
341                                   SUN4I_HDMI_PAD_CTRL1_REG_DEN |
342                                   SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT |
343                                   SUN4I_HDMI_PAD_CTRL1_EMP_OPT |
344                                   SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT |
345                                   SUN4I_HDMI_PAD_CTRL1_AMP_OPT,
346         .pll_ctrl_init_val      = SUN4I_HDMI_PLL_CTRL_VCO_S(8) |
347                                   SUN4I_HDMI_PLL_CTRL_CS(7) |
348                                   SUN4I_HDMI_PLL_CTRL_CP_S(15) |
349                                   SUN4I_HDMI_PLL_CTRL_S(7) |
350                                   SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) |
351                                   SUN4I_HDMI_PLL_CTRL_SDIV2 |
352                                   SUN4I_HDMI_PLL_CTRL_LDO2_EN |
353                                   SUN4I_HDMI_PLL_CTRL_LDO1_EN |
354                                   SUN4I_HDMI_PLL_CTRL_HV_IS_33 |
355                                   SUN4I_HDMI_PLL_CTRL_BWS |
356                                   SUN4I_HDMI_PLL_CTRL_PLL_EN,
357
358         .ddc_clk_reg            = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6),
359         .ddc_clk_pre_divider    = 2,
360         .ddc_clk_m_offset       = 1,
361
362         .field_ddc_en           = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31),
363         .field_ddc_start        = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30),
364         .field_ddc_reset        = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0),
365         .field_ddc_addr_reg     = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31),
366         .field_ddc_slave_addr   = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6),
367         .field_ddc_int_status   = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8),
368         .field_ddc_fifo_clear   = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31),
369         .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7),
370         .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3),
371         .field_ddc_byte_count   = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9),
372         .field_ddc_cmd          = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2),
373         .field_ddc_sda_en       = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9),
374         .field_ddc_sck_en       = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8),
375
376         .ddc_fifo_reg           = SUN4I_HDMI_DDC_FIFO_DATA_REG,
377         .ddc_fifo_has_dir       = true,
378 };
379
380 static const struct sun4i_hdmi_variant sun6i_variant = {
381         .has_ddc_parent_clk     = true,
382         .has_reset_control      = true,
383         .pad_ctrl0_init_val     = 0xff |
384                                   SUN4I_HDMI_PAD_CTRL0_TXEN |
385                                   SUN4I_HDMI_PAD_CTRL0_CKEN |
386                                   SUN4I_HDMI_PAD_CTRL0_PWENG |
387                                   SUN4I_HDMI_PAD_CTRL0_PWEND |
388                                   SUN4I_HDMI_PAD_CTRL0_PWENC |
389                                   SUN4I_HDMI_PAD_CTRL0_LDODEN |
390                                   SUN4I_HDMI_PAD_CTRL0_LDOCEN,
391         .pad_ctrl1_init_val     = SUN4I_HDMI_PAD_CTRL1_REG_AMP(6) |
392                                   SUN4I_HDMI_PAD_CTRL1_REG_EMP(4) |
393                                   SUN4I_HDMI_PAD_CTRL1_REG_DENCK |
394                                   SUN4I_HDMI_PAD_CTRL1_REG_DEN |
395                                   SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT |
396                                   SUN4I_HDMI_PAD_CTRL1_EMP_OPT |
397                                   SUN4I_HDMI_PAD_CTRL1_PWSDT |
398                                   SUN4I_HDMI_PAD_CTRL1_PWSCK |
399                                   SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT |
400                                   SUN4I_HDMI_PAD_CTRL1_AMP_OPT |
401                                   SUN4I_HDMI_PAD_CTRL1_UNKNOWN,
402         .pll_ctrl_init_val      = SUN4I_HDMI_PLL_CTRL_VCO_S(8) |
403                                   SUN4I_HDMI_PLL_CTRL_CS(3) |
404                                   SUN4I_HDMI_PLL_CTRL_CP_S(10) |
405                                   SUN4I_HDMI_PLL_CTRL_S(4) |
406                                   SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) |
407                                   SUN4I_HDMI_PLL_CTRL_SDIV2 |
408                                   SUN4I_HDMI_PLL_CTRL_LDO2_EN |
409                                   SUN4I_HDMI_PLL_CTRL_LDO1_EN |
410                                   SUN4I_HDMI_PLL_CTRL_HV_IS_33 |
411                                   SUN4I_HDMI_PLL_CTRL_PLL_EN,
412
413         .ddc_clk_reg            = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6),
414         .ddc_clk_pre_divider    = 1,
415         .ddc_clk_m_offset       = 2,
416
417         .tmds_clk_div_offset    = 1,
418
419         .field_ddc_en           = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0),
420         .field_ddc_start        = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27),
421         .field_ddc_reset        = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31),
422         .field_ddc_addr_reg     = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31),
423         .field_ddc_slave_addr   = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7),
424         .field_ddc_int_status   = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8),
425         .field_ddc_fifo_clear   = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18),
426         .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7),
427         .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3),
428         .field_ddc_byte_count   = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25),
429         .field_ddc_cmd          = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2),
430         .field_ddc_sda_en       = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6),
431         .field_ddc_sck_en       = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4),
432
433         .ddc_fifo_reg           = SUN6I_HDMI_DDC_FIFO_DATA_REG,
434         .ddc_fifo_thres_incl    = true,
435 };
436
437 static const struct regmap_config sun4i_hdmi_regmap_config = {
438         .reg_bits       = 32,
439         .val_bits       = 32,
440         .reg_stride     = 4,
441         .max_register   = 0x580,
442 };
443
444 static int sun4i_hdmi_bind(struct device *dev, struct device *master,
445                            void *data)
446 {
447         struct platform_device *pdev = to_platform_device(dev);
448         struct drm_device *drm = data;
449         struct sun4i_drv *drv = drm->dev_private;
450         struct sun4i_hdmi *hdmi;
451         struct resource *res;
452         u32 reg;
453         int ret;
454
455         hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
456         if (!hdmi)
457                 return -ENOMEM;
458         dev_set_drvdata(dev, hdmi);
459         hdmi->dev = dev;
460         hdmi->drv = drv;
461
462         hdmi->variant = of_device_get_match_data(dev);
463         if (!hdmi->variant)
464                 return -EINVAL;
465
466         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
467         hdmi->base = devm_ioremap_resource(dev, res);
468         if (IS_ERR(hdmi->base)) {
469                 dev_err(dev, "Couldn't map the HDMI encoder registers\n");
470                 return PTR_ERR(hdmi->base);
471         }
472
473         if (hdmi->variant->has_reset_control) {
474                 hdmi->reset = devm_reset_control_get(dev, NULL);
475                 if (IS_ERR(hdmi->reset)) {
476                         dev_err(dev, "Couldn't get the HDMI reset control\n");
477                         return PTR_ERR(hdmi->reset);
478                 }
479
480                 ret = reset_control_deassert(hdmi->reset);
481                 if (ret) {
482                         dev_err(dev, "Couldn't deassert HDMI reset\n");
483                         return ret;
484                 }
485         }
486
487         hdmi->bus_clk = devm_clk_get(dev, "ahb");
488         if (IS_ERR(hdmi->bus_clk)) {
489                 dev_err(dev, "Couldn't get the HDMI bus clock\n");
490                 ret = PTR_ERR(hdmi->bus_clk);
491                 goto err_assert_reset;
492         }
493         clk_prepare_enable(hdmi->bus_clk);
494
495         hdmi->mod_clk = devm_clk_get(dev, "mod");
496         if (IS_ERR(hdmi->mod_clk)) {
497                 dev_err(dev, "Couldn't get the HDMI mod clock\n");
498                 ret = PTR_ERR(hdmi->mod_clk);
499                 goto err_disable_bus_clk;
500         }
501         clk_prepare_enable(hdmi->mod_clk);
502
503         hdmi->pll0_clk = devm_clk_get(dev, "pll-0");
504         if (IS_ERR(hdmi->pll0_clk)) {
505                 dev_err(dev, "Couldn't get the HDMI PLL 0 clock\n");
506                 ret = PTR_ERR(hdmi->pll0_clk);
507                 goto err_disable_mod_clk;
508         }
509
510         hdmi->pll1_clk = devm_clk_get(dev, "pll-1");
511         if (IS_ERR(hdmi->pll1_clk)) {
512                 dev_err(dev, "Couldn't get the HDMI PLL 1 clock\n");
513                 ret = PTR_ERR(hdmi->pll1_clk);
514                 goto err_disable_mod_clk;
515         }
516
517         hdmi->regmap = devm_regmap_init_mmio(dev, hdmi->base,
518                                              &sun4i_hdmi_regmap_config);
519         if (IS_ERR(hdmi->regmap)) {
520                 dev_err(dev, "Couldn't create HDMI encoder regmap\n");
521                 return PTR_ERR(hdmi->regmap);
522         }
523
524         ret = sun4i_tmds_create(hdmi);
525         if (ret) {
526                 dev_err(dev, "Couldn't create the TMDS clock\n");
527                 goto err_disable_mod_clk;
528         }
529
530         if (hdmi->variant->has_ddc_parent_clk) {
531                 hdmi->ddc_parent_clk = devm_clk_get(dev, "ddc");
532                 if (IS_ERR(hdmi->ddc_parent_clk)) {
533                         dev_err(dev, "Couldn't get the HDMI DDC clock\n");
534                         return PTR_ERR(hdmi->ddc_parent_clk);
535                 }
536         } else {
537                 hdmi->ddc_parent_clk = hdmi->tmds_clk;
538         }
539
540         writel(SUN4I_HDMI_CTRL_ENABLE, hdmi->base + SUN4I_HDMI_CTRL_REG);
541
542         writel(hdmi->variant->pad_ctrl0_init_val,
543                hdmi->base + SUN4I_HDMI_PAD_CTRL0_REG);
544
545         reg = readl(hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
546         reg &= SUN4I_HDMI_PLL_CTRL_DIV_MASK;
547         reg |= hdmi->variant->pll_ctrl_init_val;
548         writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
549
550         ret = sun4i_hdmi_i2c_create(dev, hdmi);
551         if (ret) {
552                 dev_err(dev, "Couldn't create the HDMI I2C adapter\n");
553                 goto err_disable_mod_clk;
554         }
555
556         drm_encoder_helper_add(&hdmi->encoder,
557                                &sun4i_hdmi_helper_funcs);
558         ret = drm_encoder_init(drm,
559                                &hdmi->encoder,
560                                &sun4i_hdmi_funcs,
561                                DRM_MODE_ENCODER_TMDS,
562                                NULL);
563         if (ret) {
564                 dev_err(dev, "Couldn't initialise the HDMI encoder\n");
565                 goto err_del_i2c_adapter;
566         }
567
568         hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm,
569                                                                   dev->of_node);
570         if (!hdmi->encoder.possible_crtcs) {
571                 ret = -EPROBE_DEFER;
572                 goto err_del_i2c_adapter;
573         }
574
575 #ifdef CONFIG_DRM_SUN4I_HDMI_CEC
576         hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops,
577                 hdmi, "sun4i", CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
578                 CEC_CAP_PASSTHROUGH | CEC_CAP_RC);
579         ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
580         if (ret < 0)
581                 goto err_cleanup_connector;
582         writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX,
583                hdmi->base + SUN4I_HDMI_CEC);
584 #endif
585
586         drm_connector_helper_add(&hdmi->connector,
587                                  &sun4i_hdmi_connector_helper_funcs);
588         ret = drm_connector_init(drm, &hdmi->connector,
589                                  &sun4i_hdmi_connector_funcs,
590                                  DRM_MODE_CONNECTOR_HDMIA);
591         if (ret) {
592                 dev_err(dev,
593                         "Couldn't initialise the HDMI connector\n");
594                 goto err_cleanup_connector;
595         }
596
597         /* There is no HPD interrupt, so we need to poll the controller */
598         hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
599                 DRM_CONNECTOR_POLL_DISCONNECT;
600
601         ret = cec_register_adapter(hdmi->cec_adap, dev);
602         if (ret < 0)
603                 goto err_cleanup_connector;
604         drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
605
606         return 0;
607
608 err_cleanup_connector:
609         cec_delete_adapter(hdmi->cec_adap);
610         drm_encoder_cleanup(&hdmi->encoder);
611 err_del_i2c_adapter:
612         i2c_del_adapter(hdmi->i2c);
613 err_disable_mod_clk:
614         clk_disable_unprepare(hdmi->mod_clk);
615 err_disable_bus_clk:
616         clk_disable_unprepare(hdmi->bus_clk);
617 err_assert_reset:
618         reset_control_assert(hdmi->reset);
619         return ret;
620 }
621
622 static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
623                             void *data)
624 {
625         struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
626
627         cec_unregister_adapter(hdmi->cec_adap);
628         drm_connector_cleanup(&hdmi->connector);
629         drm_encoder_cleanup(&hdmi->encoder);
630         i2c_del_adapter(hdmi->i2c);
631         clk_disable_unprepare(hdmi->mod_clk);
632         clk_disable_unprepare(hdmi->bus_clk);
633 }
634
635 static const struct component_ops sun4i_hdmi_ops = {
636         .bind   = sun4i_hdmi_bind,
637         .unbind = sun4i_hdmi_unbind,
638 };
639
640 static int sun4i_hdmi_probe(struct platform_device *pdev)
641 {
642         return component_add(&pdev->dev, &sun4i_hdmi_ops);
643 }
644
645 static int sun4i_hdmi_remove(struct platform_device *pdev)
646 {
647         component_del(&pdev->dev, &sun4i_hdmi_ops);
648
649         return 0;
650 }
651
652 static const struct of_device_id sun4i_hdmi_of_table[] = {
653         { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_variant, },
654         { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, },
655         { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, },
656         { }
657 };
658 MODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table);
659
660 static struct platform_driver sun4i_hdmi_driver = {
661         .probe          = sun4i_hdmi_probe,
662         .remove         = sun4i_hdmi_remove,
663         .driver         = {
664                 .name           = "sun4i-hdmi",
665                 .of_match_table = sun4i_hdmi_of_table,
666         },
667 };
668 module_platform_driver(sun4i_hdmi_driver);
669
670 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
671 MODULE_DESCRIPTION("Allwinner A10 HDMI Driver");
672 MODULE_LICENSE("GPL");