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