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.output) {
 116                                int value = gpio.value ? 0 : 1;
 117
 118                                gpio_set_value_cansleep(gpio.num, value);
 119                        }
 120
 121                        gpio_free(gpio.num);
 122                };
 123
 124                DBG("gpio off");
 125        }
 126
 127        return 0;
 128err:
 129        while (i--)
 130                gpio_free(config->gpios[i].num);
 131
 132        return ret;
 133}
 134
 135static int hpd_enable(struct hdmi_connector *hdmi_connector)
 136{
 137        struct hdmi *hdmi = hdmi_connector->hdmi;
 138        const struct hdmi_platform_config *config = hdmi->config;
 139        struct device *dev = &hdmi->pdev->dev;
 140        uint32_t hpd_ctrl;
 141        int i, ret;
 142        unsigned long flags;
 143
 144        for (i = 0; i < config->hpd_reg_cnt; i++) {
 145                ret = regulator_enable(hdmi->hpd_regs[i]);
 146                if (ret) {
 147                        dev_err(dev, "failed to enable hpd regulator: %s (%d)\n",
 148                                        config->hpd_reg_names[i], ret);
 149                        goto fail;
 150                }
 151        }
 152
 153        ret = pinctrl_pm_select_default_state(dev);
 154        if (ret) {
 155                dev_err(dev, "pinctrl state chg failed: %d\n", ret);
 156                goto fail;
 157        }
 158
 159        ret = gpio_config(hdmi, true);
 160        if (ret) {
 161                dev_err(dev, "failed to configure GPIOs: %d\n", ret);
 162                goto fail;
 163        }
 164
 165        for (i = 0; i < config->hpd_clk_cnt; i++) {
 166                if (config->hpd_freq && config->hpd_freq[i]) {
 167                        ret = clk_set_rate(hdmi->hpd_clks[i],
 168                                        config->hpd_freq[i]);
 169                        if (ret)
 170                                dev_warn(dev, "failed to set clk %s (%d)\n",
 171                                                config->hpd_clk_names[i], ret);
 172                }
 173
 174                ret = clk_prepare_enable(hdmi->hpd_clks[i]);
 175                if (ret) {
 176                        dev_err(dev, "failed to enable hpd clk: %s (%d)\n",
 177                                        config->hpd_clk_names[i], ret);
 178                        goto fail;
 179                }
 180        }
 181
 182        msm_hdmi_set_mode(hdmi, false);
 183        msm_hdmi_phy_reset(hdmi);
 184        msm_hdmi_set_mode(hdmi, true);
 185
 186        hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
 187
 188        /* enable HPD events: */
 189        hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
 190                        HDMI_HPD_INT_CTRL_INT_CONNECT |
 191                        HDMI_HPD_INT_CTRL_INT_EN);
 192
 193        /* set timeout to 4.1ms (max) for hardware debounce */
 194        spin_lock_irqsave(&hdmi->reg_lock, flags);
 195        hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
 196        hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
 197
 198        /* Toggle HPD circuit to trigger HPD sense */
 199        hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
 200                        ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
 201        hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
 202                        HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
 203        spin_unlock_irqrestore(&hdmi->reg_lock, flags);
 204
 205        return 0;
 206
 207fail:
 208        return ret;
 209}
 210
 211static void hdp_disable(struct hdmi_connector *hdmi_connector)
 212{
 213        struct hdmi *hdmi = hdmi_connector->hdmi;
 214        const struct hdmi_platform_config *config = hdmi->config;
 215        struct device *dev = &hdmi->pdev->dev;
 216        int i, ret = 0;
 217
 218        /* Disable HPD interrupt */
 219        hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
 220
 221        msm_hdmi_set_mode(hdmi, false);
 222
 223        for (i = 0; i < config->hpd_clk_cnt; i++)
 224                clk_disable_unprepare(hdmi->hpd_clks[i]);
 225
 226        ret = gpio_config(hdmi, false);
 227        if (ret)
 228                dev_warn(dev, "failed to unconfigure GPIOs: %d\n", ret);
 229
 230        ret = pinctrl_pm_select_sleep_state(dev);
 231        if (ret)
 232                dev_warn(dev, "pinctrl state chg failed: %d\n", ret);
 233
 234        for (i = 0; i < config->hpd_reg_cnt; i++) {
 235                ret = regulator_disable(hdmi->hpd_regs[i]);
 236                if (ret)
 237                        dev_warn(dev, "failed to disable hpd regulator: %s (%d)\n",
 238                                        config->hpd_reg_names[i], ret);
 239        }
 240}
 241
 242static void
 243msm_hdmi_hotplug_work(struct work_struct *work)
 244{
 245        struct hdmi_connector *hdmi_connector =
 246                container_of(work, struct hdmi_connector, hpd_work);
 247        struct drm_connector *connector = &hdmi_connector->base;
 248        drm_helper_hpd_irq_event(connector->dev);
 249}
 250
 251void msm_hdmi_connector_irq(struct drm_connector *connector)
 252{
 253        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 254        struct hdmi *hdmi = hdmi_connector->hdmi;
 255        uint32_t hpd_int_status, hpd_int_ctrl;
 256
 257        /* Process HPD: */
 258        hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
 259        hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
 260
 261        if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
 262                        (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
 263                bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
 264
 265                /* ack & disable (temporarily) HPD events: */
 266                hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
 267                        HDMI_HPD_INT_CTRL_INT_ACK);
 268
 269                DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
 270
 271                /* detect disconnect if we are connected or visa versa: */
 272                hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
 273                if (!detected)
 274                        hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
 275                hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
 276
 277                queue_work(hdmi->workq, &hdmi_connector->hpd_work);
 278        }
 279}
 280
 281static enum drm_connector_status detect_reg(struct hdmi *hdmi)
 282{
 283        uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
 284        return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
 285                        connector_status_connected : connector_status_disconnected;
 286}
 287
 288#define HPD_GPIO_INDEX  2
 289static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
 290{
 291        const struct hdmi_platform_config *config = hdmi->config;
 292        struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
 293
 294        return gpio_get_value(hpd_gpio.num) ?
 295                        connector_status_connected :
 296                        connector_status_disconnected;
 297}
 298
 299static enum drm_connector_status hdmi_connector_detect(
 300                struct drm_connector *connector, bool force)
 301{
 302        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 303        struct hdmi *hdmi = hdmi_connector->hdmi;
 304        const struct hdmi_platform_config *config = hdmi->config;
 305        struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
 306        enum drm_connector_status stat_gpio, stat_reg;
 307        int retry = 20;
 308
 309        /*
 310         * some platforms may not have hpd gpio. Rely only on the status
 311         * provided by REG_HDMI_HPD_INT_STATUS in this case.
 312         */
 313        if (hpd_gpio.num == -1)
 314                return detect_reg(hdmi);
 315
 316        do {
 317                stat_gpio = detect_gpio(hdmi);
 318                stat_reg  = detect_reg(hdmi);
 319
 320                if (stat_gpio == stat_reg)
 321                        break;
 322
 323                mdelay(10);
 324        } while (--retry);
 325
 326        /* the status we get from reading gpio seems to be more reliable,
 327         * so trust that one the most if we didn't manage to get hdmi and
 328         * gpio status to agree:
 329         */
 330        if (stat_gpio != stat_reg) {
 331                DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
 332                DBG("hpd gpio tells us: %d", stat_gpio);
 333        }
 334
 335        return stat_gpio;
 336}
 337
 338static void hdmi_connector_destroy(struct drm_connector *connector)
 339{
 340        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 341
 342        hdp_disable(hdmi_connector);
 343
 344        drm_connector_unregister(connector);
 345        drm_connector_cleanup(connector);
 346
 347        kfree(hdmi_connector);
 348}
 349
 350static int msm_hdmi_connector_get_modes(struct drm_connector *connector)
 351{
 352        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 353        struct hdmi *hdmi = hdmi_connector->hdmi;
 354        struct edid *edid;
 355        uint32_t hdmi_ctrl;
 356        int ret = 0;
 357
 358        hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
 359        hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
 360
 361        edid = drm_get_edid(connector, hdmi->i2c);
 362
 363        hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
 364
 365        hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
 366        drm_mode_connector_update_edid_property(connector, edid);
 367
 368        if (edid) {
 369                ret = drm_add_edid_modes(connector, edid);
 370                kfree(edid);
 371        }
 372
 373        return ret;
 374}
 375
 376static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
 377                                 struct drm_display_mode *mode)
 378{
 379        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 380        struct hdmi *hdmi = hdmi_connector->hdmi;
 381        const struct hdmi_platform_config *config = hdmi->config;
 382        struct msm_drm_private *priv = connector->dev->dev_private;
 383        struct msm_kms *kms = priv->kms;
 384        long actual, requested;
 385
 386        requested = 1000 * mode->clock;
 387        actual = kms->funcs->round_pixclk(kms,
 388                        requested, hdmi_connector->hdmi->encoder);
 389
 390        /* for mdp5/apq8074, we manage our own pixel clk (as opposed to
 391         * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
 392         * instead):
 393         */
 394        if (config->pwr_clk_cnt > 0)
 395                actual = clk_round_rate(hdmi->pwr_clks[0], actual);
 396
 397        DBG("requested=%ld, actual=%ld", requested, actual);
 398
 399        if (actual != requested)
 400                return MODE_CLOCK_RANGE;
 401
 402        return 0;
 403}
 404
 405static struct drm_encoder *
 406msm_hdmi_connector_best_encoder(struct drm_connector *connector)
 407{
 408        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
 409        return hdmi_connector->hdmi->encoder;
 410}
 411
 412static const struct drm_connector_funcs hdmi_connector_funcs = {
 413        .dpms = drm_atomic_helper_connector_dpms,
 414        .detect = hdmi_connector_detect,
 415        .fill_modes = drm_helper_probe_single_connector_modes,
 416        .destroy = hdmi_connector_destroy,
 417        .reset = drm_atomic_helper_connector_reset,
 418        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 419        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 420};
 421
 422static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
 423        .get_modes = msm_hdmi_connector_get_modes,
 424        .mode_valid = msm_hdmi_connector_mode_valid,
 425        .best_encoder = msm_hdmi_connector_best_encoder,
 426};
 427
 428/* initialize connector */
 429struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi)
 430{
 431        struct drm_connector *connector = NULL;
 432        struct hdmi_connector *hdmi_connector;
 433        int ret;
 434
 435        hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
 436        if (!hdmi_connector) {
 437                ret = -ENOMEM;
 438                goto fail;
 439        }
 440
 441        hdmi_connector->hdmi = hdmi;
 442        INIT_WORK(&hdmi_connector->hpd_work, msm_hdmi_hotplug_work);
 443
 444        connector = &hdmi_connector->base;
 445
 446        drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs,
 447                        DRM_MODE_CONNECTOR_HDMIA);
 448        drm_connector_helper_add(connector, &msm_hdmi_connector_helper_funcs);
 449
 450        connector->polled = DRM_CONNECTOR_POLL_CONNECT |
 451                        DRM_CONNECTOR_POLL_DISCONNECT;
 452
 453        connector->interlace_allowed = 0;
 454        connector->doublescan_allowed = 0;
 455
 456        drm_connector_register(connector);
 457
 458        ret = hpd_enable(hdmi_connector);
 459        if (ret) {
 460                dev_err(&hdmi->pdev->dev, "failed to enable HPD: %d\n", ret);
 461                goto fail;
 462        }
 463
 464        drm_mode_connector_attach_encoder(connector, hdmi->encoder);
 465
 466        return connector;
 467
 468fail:
 469        if (connector)
 470                hdmi_connector_destroy(connector);
 471
 472        return ERR_PTR(ret);
 473}
 474