linux/drivers/gpu/drm/msm/hdmi/hdmi.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 "hdmi.h"
  19
  20void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
  21{
  22        uint32_t ctrl = 0;
  23
  24        if (power_on) {
  25                ctrl |= HDMI_CTRL_ENABLE;
  26                if (!hdmi->hdmi_mode) {
  27                        ctrl |= HDMI_CTRL_HDMI;
  28                        hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
  29                        ctrl &= ~HDMI_CTRL_HDMI;
  30                } else {
  31                        ctrl |= HDMI_CTRL_HDMI;
  32                }
  33        } else {
  34                ctrl = HDMI_CTRL_HDMI;
  35        }
  36
  37        hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
  38        DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
  39                        power_on ? "Enable" : "Disable", ctrl);
  40}
  41
  42irqreturn_t hdmi_irq(int irq, void *dev_id)
  43{
  44        struct hdmi *hdmi = dev_id;
  45
  46        /* Process HPD: */
  47        hdmi_connector_irq(hdmi->connector);
  48
  49        /* Process DDC: */
  50        hdmi_i2c_irq(hdmi->i2c);
  51
  52        /* TODO audio.. */
  53
  54        return IRQ_HANDLED;
  55}
  56
  57void hdmi_destroy(struct kref *kref)
  58{
  59        struct hdmi *hdmi = container_of(kref, struct hdmi, refcount);
  60        struct hdmi_phy *phy = hdmi->phy;
  61
  62        if (phy)
  63                phy->funcs->destroy(phy);
  64
  65        if (hdmi->i2c)
  66                hdmi_i2c_destroy(hdmi->i2c);
  67
  68        platform_set_drvdata(hdmi->pdev, NULL);
  69}
  70
  71/* initialize connector */
  72struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
  73{
  74        struct hdmi *hdmi = NULL;
  75        struct msm_drm_private *priv = dev->dev_private;
  76        struct platform_device *pdev = priv->hdmi_pdev;
  77        struct hdmi_platform_config *config;
  78        int i, ret;
  79
  80        if (!pdev) {
  81                dev_err(dev->dev, "no hdmi device\n");
  82                ret = -ENXIO;
  83                goto fail;
  84        }
  85
  86        config = pdev->dev.platform_data;
  87
  88        hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
  89        if (!hdmi) {
  90                ret = -ENOMEM;
  91                goto fail;
  92        }
  93
  94        kref_init(&hdmi->refcount);
  95
  96        hdmi->dev = dev;
  97        hdmi->pdev = pdev;
  98        hdmi->config = config;
  99        hdmi->encoder = encoder;
 100
 101        hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
 102
 103        /* not sure about which phy maps to which msm.. probably I miss some */
 104        if (config->phy_init)
 105                hdmi->phy = config->phy_init(hdmi);
 106        else
 107                hdmi->phy = ERR_PTR(-ENXIO);
 108
 109        if (IS_ERR(hdmi->phy)) {
 110                ret = PTR_ERR(hdmi->phy);
 111                dev_err(dev->dev, "failed to load phy: %d\n", ret);
 112                hdmi->phy = NULL;
 113                goto fail;
 114        }
 115
 116        hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
 117        if (IS_ERR(hdmi->mmio)) {
 118                ret = PTR_ERR(hdmi->mmio);
 119                goto fail;
 120        }
 121
 122        BUG_ON(config->hpd_reg_cnt > ARRAY_SIZE(hdmi->hpd_regs));
 123        for (i = 0; i < config->hpd_reg_cnt; i++) {
 124                struct regulator *reg;
 125
 126                reg = devm_regulator_get(&pdev->dev,
 127                                config->hpd_reg_names[i]);
 128                if (IS_ERR(reg)) {
 129                        ret = PTR_ERR(reg);
 130                        dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
 131                                        config->hpd_reg_names[i], ret);
 132                        goto fail;
 133                }
 134
 135                hdmi->hpd_regs[i] = reg;
 136        }
 137
 138        BUG_ON(config->pwr_reg_cnt > ARRAY_SIZE(hdmi->pwr_regs));
 139        for (i = 0; i < config->pwr_reg_cnt; i++) {
 140                struct regulator *reg;
 141
 142                reg = devm_regulator_get(&pdev->dev,
 143                                config->pwr_reg_names[i]);
 144                if (IS_ERR(reg)) {
 145                        ret = PTR_ERR(reg);
 146                        dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
 147                                        config->pwr_reg_names[i], ret);
 148                        goto fail;
 149                }
 150
 151                hdmi->pwr_regs[i] = reg;
 152        }
 153
 154        BUG_ON(config->hpd_clk_cnt > ARRAY_SIZE(hdmi->hpd_clks));
 155        for (i = 0; i < config->hpd_clk_cnt; i++) {
 156                struct clk *clk;
 157
 158                clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
 159                if (IS_ERR(clk)) {
 160                        ret = PTR_ERR(clk);
 161                        dev_err(dev->dev, "failed to get hpd clk: %s (%d)\n",
 162                                        config->hpd_clk_names[i], ret);
 163                        goto fail;
 164                }
 165
 166                hdmi->hpd_clks[i] = clk;
 167        }
 168
 169        BUG_ON(config->pwr_clk_cnt > ARRAY_SIZE(hdmi->pwr_clks));
 170        for (i = 0; i < config->pwr_clk_cnt; i++) {
 171                struct clk *clk;
 172
 173                clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
 174                if (IS_ERR(clk)) {
 175                        ret = PTR_ERR(clk);
 176                        dev_err(dev->dev, "failed to get pwr clk: %s (%d)\n",
 177                                        config->pwr_clk_names[i], ret);
 178                        goto fail;
 179                }
 180
 181                hdmi->pwr_clks[i] = clk;
 182        }
 183
 184        hdmi->i2c = hdmi_i2c_init(hdmi);
 185        if (IS_ERR(hdmi->i2c)) {
 186                ret = PTR_ERR(hdmi->i2c);
 187                dev_err(dev->dev, "failed to get i2c: %d\n", ret);
 188                hdmi->i2c = NULL;
 189                goto fail;
 190        }
 191
 192        hdmi->bridge = hdmi_bridge_init(hdmi);
 193        if (IS_ERR(hdmi->bridge)) {
 194                ret = PTR_ERR(hdmi->bridge);
 195                dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret);
 196                hdmi->bridge = NULL;
 197                goto fail;
 198        }
 199
 200        hdmi->connector = hdmi_connector_init(hdmi);
 201        if (IS_ERR(hdmi->connector)) {
 202                ret = PTR_ERR(hdmi->connector);
 203                dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret);
 204                hdmi->connector = NULL;
 205                goto fail;
 206        }
 207
 208        if (!config->shared_irq) {
 209                hdmi->irq = platform_get_irq(pdev, 0);
 210                if (hdmi->irq < 0) {
 211                        ret = hdmi->irq;
 212                        dev_err(dev->dev, "failed to get irq: %d\n", ret);
 213                        goto fail;
 214                }
 215
 216                ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq,
 217                                NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
 218                                "hdmi_isr", hdmi);
 219                if (ret < 0) {
 220                        dev_err(dev->dev, "failed to request IRQ%u: %d\n",
 221                                        hdmi->irq, ret);
 222                        goto fail;
 223                }
 224        }
 225
 226        encoder->bridge = hdmi->bridge;
 227
 228        priv->bridges[priv->num_bridges++]       = hdmi->bridge;
 229        priv->connectors[priv->num_connectors++] = hdmi->connector;
 230
 231        platform_set_drvdata(pdev, hdmi);
 232
 233        return hdmi;
 234
 235fail:
 236        if (hdmi) {
 237                /* bridge/connector are normally destroyed by drm: */
 238                if (hdmi->bridge)
 239                        hdmi->bridge->funcs->destroy(hdmi->bridge);
 240                if (hdmi->connector)
 241                        hdmi->connector->funcs->destroy(hdmi->connector);
 242                hdmi_destroy(&hdmi->refcount);
 243        }
 244
 245        return ERR_PTR(ret);
 246}
 247
 248/*
 249 * The hdmi device:
 250 */
 251
 252#include <linux/of_gpio.h>
 253
 254static void set_hdmi_pdev(struct drm_device *dev,
 255                struct platform_device *pdev)
 256{
 257        struct msm_drm_private *priv = dev->dev_private;
 258        priv->hdmi_pdev = pdev;
 259}
 260
 261#ifdef CONFIG_OF
 262static int get_gpio(struct device *dev, struct device_node *of_node, const char *name)
 263{
 264        int gpio = of_get_named_gpio(of_node, name, 0);
 265        if (gpio < 0) {
 266                char name2[32];
 267                snprintf(name2, sizeof(name2), "%s-gpio", name);
 268                gpio = of_get_named_gpio(of_node, name2, 0);
 269                if (gpio < 0) {
 270                        dev_err(dev, "failed to get gpio: %s (%d)\n",
 271                                        name, gpio);
 272                        gpio = -1;
 273                }
 274        }
 275        return gpio;
 276}
 277#endif
 278
 279static int hdmi_bind(struct device *dev, struct device *master, void *data)
 280{
 281        static struct hdmi_platform_config config = {};
 282#ifdef CONFIG_OF
 283        struct device_node *of_node = dev->of_node;
 284
 285        if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8074")) {
 286                static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
 287                static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
 288                static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
 289                static unsigned long hpd_clk_freq[] = {0, 19200000, 0};
 290                static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
 291                config.phy_init      = hdmi_phy_8x74_init;
 292                config.hpd_reg_names = hpd_reg_names;
 293                config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
 294                config.pwr_reg_names = pwr_reg_names;
 295                config.pwr_reg_cnt   = ARRAY_SIZE(pwr_reg_names);
 296                config.hpd_clk_names = hpd_clk_names;
 297                config.hpd_freq      = hpd_clk_freq;
 298                config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
 299                config.pwr_clk_names = pwr_clk_names;
 300                config.pwr_clk_cnt   = ARRAY_SIZE(pwr_clk_names);
 301                config.shared_irq    = true;
 302        } else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8960")) {
 303                static const char *hpd_clk_names[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
 304                static const char *hpd_reg_names[] = {"core-vdda", "hdmi-mux"};
 305                config.phy_init      = hdmi_phy_8960_init;
 306                config.hpd_reg_names = hpd_reg_names;
 307                config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
 308                config.hpd_clk_names = hpd_clk_names;
 309                config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
 310        } else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8660")) {
 311                config.phy_init      = hdmi_phy_8x60_init;
 312        } else {
 313                dev_err(dev, "unknown phy: %s\n", of_node->name);
 314        }
 315
 316        config.mmio_name     = "core_physical";
 317        config.ddc_clk_gpio  = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk");
 318        config.ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data");
 319        config.hpd_gpio      = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd");
 320        config.mux_en_gpio   = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-en");
 321        config.mux_sel_gpio  = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-sel");
 322        config.mux_lpm_gpio  = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-lpm");
 323
 324#else
 325        static const char *hpd_clk_names[] = {
 326                        "core_clk", "master_iface_clk", "slave_iface_clk",
 327        };
 328        if (cpu_is_apq8064()) {
 329                static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
 330                config.phy_init      = hdmi_phy_8960_init;
 331                config.mmio_name     = "hdmi_msm_hdmi_addr";
 332                config.hpd_reg_names = hpd_reg_names;
 333                config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
 334                config.hpd_clk_names = hpd_clk_names;
 335                config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
 336                config.ddc_clk_gpio  = 70;
 337                config.ddc_data_gpio = 71;
 338                config.hpd_gpio      = 72;
 339                config.mux_en_gpio   = -1;
 340                config.mux_sel_gpio  = -1;
 341        } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
 342                static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
 343                config.phy_init      = hdmi_phy_8960_init;
 344                config.mmio_name     = "hdmi_msm_hdmi_addr";
 345                config.hpd_reg_names = hpd_reg_names;
 346                config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
 347                config.hpd_clk_names = hpd_clk_names;
 348                config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
 349                config.ddc_clk_gpio  = 100;
 350                config.ddc_data_gpio = 101;
 351                config.hpd_gpio      = 102;
 352                config.mux_en_gpio   = -1;
 353                config.mux_sel_gpio  = -1;
 354        } else if (cpu_is_msm8x60()) {
 355                static const char *hpd_reg_names[] = {
 356                                "8901_hdmi_mvs", "8901_mpp0"
 357                };
 358                config.phy_init      = hdmi_phy_8x60_init;
 359                config.mmio_name     = "hdmi_msm_hdmi_addr";
 360                config.hpd_reg_names = hpd_reg_names;
 361                config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
 362                config.hpd_clk_names = hpd_clk_names;
 363                config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
 364                config.ddc_clk_gpio  = 170;
 365                config.ddc_data_gpio = 171;
 366                config.hpd_gpio      = 172;
 367                config.mux_en_gpio   = -1;
 368                config.mux_sel_gpio  = -1;
 369        }
 370#endif
 371        dev->platform_data = &config;
 372        set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev));
 373        return 0;
 374}
 375
 376static void hdmi_unbind(struct device *dev, struct device *master,
 377                void *data)
 378{
 379        set_hdmi_pdev(dev_get_drvdata(master), NULL);
 380}
 381
 382static const struct component_ops hdmi_ops = {
 383                .bind   = hdmi_bind,
 384                .unbind = hdmi_unbind,
 385};
 386
 387static int hdmi_dev_probe(struct platform_device *pdev)
 388{
 389        return component_add(&pdev->dev, &hdmi_ops);
 390}
 391
 392static int hdmi_dev_remove(struct platform_device *pdev)
 393{
 394        component_del(&pdev->dev, &hdmi_ops);
 395        return 0;
 396}
 397
 398static const struct of_device_id dt_match[] = {
 399        { .compatible = "qcom,hdmi-tx-8074" },
 400        { .compatible = "qcom,hdmi-tx-8960" },
 401        { .compatible = "qcom,hdmi-tx-8660" },
 402        {}
 403};
 404
 405static struct platform_driver hdmi_driver = {
 406        .probe = hdmi_dev_probe,
 407        .remove = hdmi_dev_remove,
 408        .driver = {
 409                .name = "hdmi_msm",
 410                .of_match_table = dt_match,
 411        },
 412};
 413
 414void __init hdmi_register(void)
 415{
 416        platform_driver_register(&hdmi_driver);
 417}
 418
 419void __exit hdmi_unregister(void)
 420{
 421        platform_driver_unregister(&hdmi_driver);
 422}
 423