linux/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Red Hat
   3 * Author: Rob Clark <robdclark@gmail.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published by
   7 * the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along with
  15 * this program.  If not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18#include <linux/gpio.h>
  19#include <linux/pinctrl/consumer.h>
  20
  21#include "msm_kms.h"
  22#include "hdmi.h"
  23
  24struct hdmi_connector {
  25        struct drm_connector base;
  26        struct hdmi *hdmi;
  27        struct work_struct hpd_work;
  28};
  29#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
  30
  31static void msm_hdmi_phy_reset(struct hdmi *hdmi)
  32{
  33        unsigned int val;
  34
  35        val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
  36
  37        if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
  38                /* pull low */
  39                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  40                                val & ~HDMI_PHY_CTRL_SW_RESET);
  41        } else {
  42                /* pull high */
  43                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  44                                val | HDMI_PHY_CTRL_SW_RESET);
  45        }
  46
  47        if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
  48                /* pull low */
  49                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  50                                val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
  51        } else {
  52                /* pull high */
  53                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  54                                val | HDMI_PHY_CTRL_SW_RESET_PLL);
  55        }
  56
  57        msleep(100);
  58
  59        if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
  60                /* pull high */
  61                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  62                                val | HDMI_PHY_CTRL_SW_RESET);
  63        } else {
  64                /* pull low */
  65                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  66                                val & ~HDMI_PHY_CTRL_SW_RESET);
  67        }
  68
  69        if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
  70                /* pull high */
  71                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  72                                val | HDMI_PHY_CTRL_SW_RESET_PLL);
  73        } else {
  74                /* pull low */
  75                hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
  76                                val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
  77        }
  78}
  79
  80static int gpio_config(struct hdmi *hdmi, bool on)
  81{
  82        struct device *dev = &hdmi->pdev->dev;
  83        const struct hdmi_platform_config *config = hdmi->config;
  84        int ret, i;
  85
  86        if (on) {
  87                for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
  88                        struct hdmi_gpio_data gpio = config->gpios[i];
  89
  90                        if (gpio.num != -1) {
  91                                ret = gpio_request(gpio.num, gpio.label);
  92                                if (ret) {
  93                                        dev_err(dev,
  94                                                "'%s'(%d) gpio_request failed: %d\n",
  95                                                gpio.label, gpio.num, ret);
  96                                        goto err;
  97                                }
  98
  99                                if (gpio.output) {
 100                                        gpio_direction_output(gpio.num,
 101                                                              gpio.value);
 102                                } else {
 103                                        gpio_direction_input(gpio.num);
 104                                        gpio_set_value_cansleep(gpio.num,
 105                                                                gpio.value);
 106                                }
 107                        }
 108                }
 109
 110                DBG("gpio on");
 111        } else {
 112                for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
 113                        struct hdmi_gpio_data gpio = config->gpios[i];
 114
 115                        if (gpio.num == -1)
 116                                continue;
 117
 118                        if (gpio.output) {
 119                                int value = gpio.value ? 0 : 1;
 120
 121                                gpio_set_value_cansleep(gpio.num, value);
 122                        }
 123
 124                        gpio_free(gpio.num);
 125                };
 126
 127                DBG("gpio off");
 128        }
 129
 130        return 0;
 131err:
 132        while (i--) {
 133                if (config->gpios[i].num != -1)
 134                        gpio_free(config->gpios[i].num);
 135        }
 136
 137        return ret;
 138}
 139
 140static int hpd_enable(struct hdmi_connector *hdmi_connector)
 141{
 142        struct hdmi *hdmi = hdmi_connector->hdmi;
 143        const struct hdmi_platform_config *config = hdmi->config;
 144        struct device *dev = &hdmi->pdev->dev;
 145        uint32_t hpd_ctrl;
 146        int i, ret;
 147        unsigned long flags;
 148
 149        for (i = 0; i < config->hpd_reg_cnt; i++) {
 150                ret = regulator_enable(hdmi->hpd_regs[i]);
 151                if (ret) {
 152                        dev_err(dev, "failed to enable hpd regulator: %s (%d)\n",
 153                                        config->hpd_reg_names[i], ret);
 154                        goto fail;
 155                }
 156        }
 157
 158        ret = pinctrl_pm_select_default_state(dev);
 159        if (ret) {
 160                dev_err(dev, "pinctrl state chg failed: %d\n", ret);
 161                goto fail;
 162        }
 163
 164        ret = gpio_config(hdmi, true);
 165        if (ret) {
 166                dev_err(dev, "failed to configure GPIOs: %d\n", ret);
 167                goto fail;
 168        }
 169
 170        for (i = 0; i < config->hpd_clk_cnt; i++) {
 171                if (config->hpd_freq && config->hpd_freq[i]) {
 172                        ret = clk_set_rate(hdmi->hpd_clks[i],
 173                                        config->hpd_freq[i]);
 174                        if (ret)
 175                                dev_warn(dev, "failed to set clk %s (%d)\n",
 176                                                config->hpd_clk_names[i], ret);
 177                }
 178
 179                ret = clk_prepare_enable(hdmi->hpd_clks[i]);
 180                if (ret) {
 181                        dev_err(dev, "failed to enable hpd clk: %s (%d)\n",
 182                                        config->hpd_clk_names[i], ret);
 183                        goto fail;
 184                }
 185        }
 186
 187        msm_hdmi_set_mode(hdmi, false);
 188        msm_hdmi_phy_reset(hdmi);
 189        msm_hdmi_set_mode(hdmi, true);
 190
 191        hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
 192
 193        /* enable HPD events: */
 194        hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
 195                        HDMI_HPD_INT_CTRL_INT_CONNECT |
 196                        HDMI_HPD_INT_CTRL_INT_EN);
 197
 198        /* set timeout to 4.1ms (max) for hardware debounce */
 199        spin_lock_irqsave(&hdmi->reg_lock, flags);
 200        hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
 201        hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
 202
 203        /* Toggle HPD circuit to trigger HPD sense */
 204        hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
 205                        ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
 206        hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
 207                        HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
 208        spin_unlock_irqrestore(&hdmi->reg_lock, flags);
 209
 210        return 0;
 211
 212fail:
 213        return ret;
 214}
 215
 216static void hdp_disable(struct hdmi_connector *hdmi_connector)
 217{
 218        struct hdmi *hdmi = hdmi_connector->hdmi;
 219        const struct hdmi_platform_config *config = hdmi->config;
 220        struct device *dev = &hdmi->pdev->dev;
 221        int i, ret = 0;
 222
 223        /* Disable HPD interrupt */
 224        hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
 225
 226        msm_hdmi_set_mode(hdmi, false);
 227
 228        for (i = 0; i < config->hpd_clk_cnt; i++)
 229                clk_disable_unprepare(hdmi->hpd_clks[i]);
 230
 231        ret = gpio_config(hdmi, false);
 232        if (ret)
 233                dev_warn(dev, "failed to unconfigure GPIOs: %d\n", ret);
 234
 235        ret = pinctrl_pm_select_sleep_state(dev);
 236        if (ret)
 237                dev_warn(dev, "pinctrl state chg failed: %d\n", ret);
 238
 239        for (i = 0; i < config->hpd_reg_cnt; i++) {
 240                ret = regulator_disable(hdmi->hpd_regs[i]);
 241                if (ret)
 242                        dev_warn(dev, "failed to disable hpd regulator: %s (%d)\n",
 243                                        config->hpd_reg_names[i], ret);
 244        }
 245}
 246
 247static void
 248msm_hdmi_hotplug_work(struct work_struct *work)
 249{
 250        struct hdmi_connector *hdmi_connector =
 251                container_of(work, struct hdmi_connector, hpd_work);
 252        struct drm_connector *connector = &hdmi_connector->base;
 253        drm_helper_hpd_irq_event(connector->dev);
 254}
 255
 256void msm_hdmi_connector_irq(struct drm_connector *connector)
 257{
 258        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 259        struct hdmi *hdmi = hdmi_connector->hdmi;
 260        uint32_t hpd_int_status, hpd_int_ctrl;
 261
 262        /* Process HPD: */
 263        hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
 264        hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
 265
 266        if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
 267                        (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
 268                bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
 269
 270                /* ack & disable (temporarily) HPD events: */
 271                hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
 272                        HDMI_HPD_INT_CTRL_INT_ACK);
 273
 274                DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
 275
 276                /* detect disconnect if we are connected or visa versa: */
 277                hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
 278                if (!detected)
 279                        hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
 280                hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
 281
 282                queue_work(hdmi->workq, &hdmi_connector->hpd_work);
 283        }
 284}
 285
 286static enum drm_connector_status detect_reg(struct hdmi *hdmi)
 287{
 288        uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
 289        return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
 290                        connector_status_connected : connector_status_disconnected;
 291}
 292
 293#define HPD_GPIO_INDEX  2
 294static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
 295{
 296        const struct hdmi_platform_config *config = hdmi->config;
 297        struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
 298
 299        return gpio_get_value(hpd_gpio.num) ?
 300                        connector_status_connected :
 301                        connector_status_disconnected;
 302}
 303
 304static enum drm_connector_status hdmi_connector_detect(
 305                struct drm_connector *connector, bool force)
 306{
 307        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 308        struct hdmi *hdmi = hdmi_connector->hdmi;
 309        const struct hdmi_platform_config *config = hdmi->config;
 310        struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
 311        enum drm_connector_status stat_gpio, stat_reg;
 312        int retry = 20;
 313
 314        /*
 315         * some platforms may not have hpd gpio. Rely only on the status
 316         * provided by REG_HDMI_HPD_INT_STATUS in this case.
 317         */
 318        if (hpd_gpio.num == -1)
 319                return detect_reg(hdmi);
 320
 321        do {
 322                stat_gpio = detect_gpio(hdmi);
 323                stat_reg  = detect_reg(hdmi);
 324
 325                if (stat_gpio == stat_reg)
 326                        break;
 327
 328                mdelay(10);
 329        } while (--retry);
 330
 331        /* the status we get from reading gpio seems to be more reliable,
 332         * so trust that one the most if we didn't manage to get hdmi and
 333         * gpio status to agree:
 334         */
 335        if (stat_gpio != stat_reg) {
 336                DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
 337                DBG("hpd gpio tells us: %d", stat_gpio);
 338        }
 339
 340        return stat_gpio;
 341}
 342
 343static void hdmi_connector_destroy(struct drm_connector *connector)
 344{
 345        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 346
 347        hdp_disable(hdmi_connector);
 348
 349        drm_connector_cleanup(connector);
 350
 351        kfree(hdmi_connector);
 352}
 353
 354static int msm_hdmi_connector_get_modes(struct drm_connector *connector)
 355{
 356        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 357        struct hdmi *hdmi = hdmi_connector->hdmi;
 358        struct edid *edid;
 359        uint32_t hdmi_ctrl;
 360        int ret = 0;
 361
 362        hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
 363        hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
 364
 365        edid = drm_get_edid(connector, hdmi->i2c);
 366
 367        hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
 368
 369        hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
 370        drm_mode_connector_update_edid_property(connector, edid);
 371
 372        if (edid) {
 373                ret = drm_add_edid_modes(connector, edid);
 374                kfree(edid);
 375        }
 376
 377        return ret;
 378}
 379
 380static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
 381                                 struct drm_display_mode *mode)
 382{
 383        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 384        struct hdmi *hdmi = hdmi_connector->hdmi;
 385        const struct hdmi_platform_config *config = hdmi->config;
 386        struct msm_drm_private *priv = connector->dev->dev_private;
 387        struct msm_kms *kms = priv->kms;
 388        long actual, requested;
 389
 390        requested = 1000 * mode->clock;
 391        actual = kms->funcs->round_pixclk(kms,
 392                        requested, hdmi_connector->hdmi->encoder);
 393
 394        /* for mdp5/apq8074, we manage our own pixel clk (as opposed to
 395         * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
 396         * instead):
 397         */
 398        if (config->pwr_clk_cnt > 0)
 399                actual = clk_round_rate(hdmi->pwr_clks[0], actual);
 400
 401        DBG("requested=%ld, actual=%ld", requested, actual);
 402
 403        if (actual != requested)
 404                return MODE_CLOCK_RANGE;
 405
 406        return 0;
 407}
 408
 409static const struct drm_connector_funcs hdmi_connector_funcs = {
 410        .dpms = drm_atomic_helper_connector_dpms,
 411        .detect = hdmi_connector_detect,
 412        .fill_modes = drm_helper_probe_single_connector_modes,
 413        .destroy = hdmi_connector_destroy,
 414        .reset = drm_atomic_helper_connector_reset,
 415        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 416        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 417};
 418
 419static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
 420        .get_modes = msm_hdmi_connector_get_modes,
 421        .mode_valid = msm_hdmi_connector_mode_valid,
 422};
 423
 424/* initialize connector */
 425struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi)
 426{
 427        struct drm_connector *connector = NULL;
 428        struct hdmi_connector *hdmi_connector;
 429        int ret;
 430
 431        hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
 432        if (!hdmi_connector)
 433                return ERR_PTR(-ENOMEM);
 434
 435        hdmi_connector->hdmi = hdmi;
 436        INIT_WORK(&hdmi_connector->hpd_work, msm_hdmi_hotplug_work);
 437
 438        connector = &hdmi_connector->base;
 439
 440        drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs,
 441                        DRM_MODE_CONNECTOR_HDMIA);
 442        drm_connector_helper_add(connector, &msm_hdmi_connector_helper_funcs);
 443
 444        connector->polled = DRM_CONNECTOR_POLL_CONNECT |
 445                        DRM_CONNECTOR_POLL_DISCONNECT;
 446
 447        connector->interlace_allowed = 0;
 448        connector->doublescan_allowed = 0;
 449
 450        ret = hpd_enable(hdmi_connector);
 451        if (ret) {
 452                dev_err(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret);
 453                return ERR_PTR(ret);
 454        }
 455
 456        drm_mode_connector_attach_encoder(connector, hdmi->encoder);
 457
 458        return connector;
 459}
 460