linux/arch/arm/mach-omap2/display.c
<<
>>
Prefs
   1/*
   2 * OMAP2plus display device setup / initialization.
   3 *
   4 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
   5 *      Senthilvadivu Guruswamy
   6 *      Sumit Semwal
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  13 * kind, whether express or implied; without even the implied warranty
  14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include <linux/string.h>
  19#include <linux/kernel.h>
  20#include <linux/init.h>
  21#include <linux/platform_device.h>
  22#include <linux/io.h>
  23#include <linux/clk.h>
  24#include <linux/err.h>
  25#include <linux/delay.h>
  26
  27#include <video/omapdss.h>
  28#include <plat/omap_hwmod.h>
  29#include <plat/omap_device.h>
  30#include <plat/omap-pm.h>
  31#include "common.h"
  32
  33#include "iomap.h"
  34#include "mux.h"
  35#include "control.h"
  36#include "display.h"
  37
  38#define DISPC_CONTROL           0x0040
  39#define DISPC_CONTROL2          0x0238
  40#define DISPC_IRQSTATUS         0x0018
  41
  42#define DSS_SYSCONFIG           0x10
  43#define DSS_SYSSTATUS           0x14
  44#define DSS_CONTROL             0x40
  45#define DSS_SDI_CONTROL         0x44
  46#define DSS_PLL_CONTROL         0x48
  47
  48#define LCD_EN_MASK             (0x1 << 0)
  49#define DIGIT_EN_MASK           (0x1 << 1)
  50
  51#define FRAMEDONE_IRQ_SHIFT     0
  52#define EVSYNC_EVEN_IRQ_SHIFT   2
  53#define EVSYNC_ODD_IRQ_SHIFT    3
  54#define FRAMEDONE2_IRQ_SHIFT    22
  55#define FRAMEDONETV_IRQ_SHIFT   24
  56
  57/*
  58 * FRAMEDONE_IRQ_TIMEOUT: how long (in milliseconds) to wait during DISPC
  59 *     reset before deciding that something has gone wrong
  60 */
  61#define FRAMEDONE_IRQ_TIMEOUT           100
  62
  63static struct platform_device omap_display_device = {
  64        .name          = "omapdss",
  65        .id            = -1,
  66        .dev            = {
  67                .platform_data = NULL,
  68        },
  69};
  70
  71struct omap_dss_hwmod_data {
  72        const char *oh_name;
  73        const char *dev_name;
  74        const int id;
  75};
  76
  77static const struct omap_dss_hwmod_data omap2_dss_hwmod_data[] __initdata = {
  78        { "dss_core", "omapdss_dss", -1 },
  79        { "dss_dispc", "omapdss_dispc", -1 },
  80        { "dss_rfbi", "omapdss_rfbi", -1 },
  81        { "dss_venc", "omapdss_venc", -1 },
  82};
  83
  84static const struct omap_dss_hwmod_data omap3_dss_hwmod_data[] __initdata = {
  85        { "dss_core", "omapdss_dss", -1 },
  86        { "dss_dispc", "omapdss_dispc", -1 },
  87        { "dss_rfbi", "omapdss_rfbi", -1 },
  88        { "dss_venc", "omapdss_venc", -1 },
  89        { "dss_dsi1", "omapdss_dsi", 0 },
  90};
  91
  92static const struct omap_dss_hwmod_data omap4_dss_hwmod_data[] __initdata = {
  93        { "dss_core", "omapdss_dss", -1 },
  94        { "dss_dispc", "omapdss_dispc", -1 },
  95        { "dss_rfbi", "omapdss_rfbi", -1 },
  96        { "dss_venc", "omapdss_venc", -1 },
  97        { "dss_dsi1", "omapdss_dsi", 0 },
  98        { "dss_dsi2", "omapdss_dsi", 1 },
  99        { "dss_hdmi", "omapdss_hdmi", -1 },
 100};
 101
 102static void __init omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
 103{
 104        u32 reg;
 105        u16 control_i2c_1;
 106
 107        omap_mux_init_signal("hdmi_cec",
 108                        OMAP_PIN_INPUT_PULLUP);
 109        omap_mux_init_signal("hdmi_ddc_scl",
 110                        OMAP_PIN_INPUT_PULLUP);
 111        omap_mux_init_signal("hdmi_ddc_sda",
 112                        OMAP_PIN_INPUT_PULLUP);
 113
 114        /*
 115         * CONTROL_I2C_1: HDMI_DDC_SDA_PULLUPRESX (bit 28) and
 116         * HDMI_DDC_SCL_PULLUPRESX (bit 24) are set to disable
 117         * internal pull up resistor.
 118         */
 119        if (flags & OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP) {
 120                control_i2c_1 = OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1;
 121                reg = omap4_ctrl_pad_readl(control_i2c_1);
 122                reg |= (OMAP4_HDMI_DDC_SDA_PULLUPRESX_MASK |
 123                        OMAP4_HDMI_DDC_SCL_PULLUPRESX_MASK);
 124                        omap4_ctrl_pad_writel(reg, control_i2c_1);
 125        }
 126}
 127
 128static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
 129{
 130        u32 enable_mask, enable_shift;
 131        u32 pipd_mask, pipd_shift;
 132        u32 reg;
 133
 134        if (dsi_id == 0) {
 135                enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
 136                enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
 137                pipd_mask = OMAP4_DSI1_PIPD_MASK;
 138                pipd_shift = OMAP4_DSI1_PIPD_SHIFT;
 139        } else if (dsi_id == 1) {
 140                enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
 141                enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
 142                pipd_mask = OMAP4_DSI2_PIPD_MASK;
 143                pipd_shift = OMAP4_DSI2_PIPD_SHIFT;
 144        } else {
 145                return -ENODEV;
 146        }
 147
 148        reg = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
 149
 150        reg &= ~enable_mask;
 151        reg &= ~pipd_mask;
 152
 153        reg |= (lanes << enable_shift) & enable_mask;
 154        reg |= (lanes << pipd_shift) & pipd_mask;
 155
 156        omap4_ctrl_pad_writel(reg, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
 157
 158        return 0;
 159}
 160
 161int __init omap_hdmi_init(enum omap_hdmi_flags flags)
 162{
 163        if (cpu_is_omap44xx())
 164                omap4_hdmi_mux_pads(flags);
 165
 166        return 0;
 167}
 168
 169static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
 170{
 171        if (cpu_is_omap44xx())
 172                return omap4_dsi_mux_pads(dsi_id, lane_mask);
 173
 174        return 0;
 175}
 176
 177static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask)
 178{
 179        if (cpu_is_omap44xx())
 180                omap4_dsi_mux_pads(dsi_id, 0);
 181}
 182
 183static int omap_dss_set_min_bus_tput(struct device *dev, unsigned long tput)
 184{
 185        return omap_pm_set_min_bus_tput(dev, OCP_INITIATOR_AGENT, tput);
 186}
 187
 188static struct platform_device *create_dss_pdev(const char *pdev_name,
 189                int pdev_id, const char *oh_name, void *pdata, int pdata_len,
 190                struct platform_device *parent)
 191{
 192        struct platform_device *pdev;
 193        struct omap_device *od;
 194        struct omap_hwmod *ohs[1];
 195        struct omap_hwmod *oh;
 196        int r;
 197
 198        oh = omap_hwmod_lookup(oh_name);
 199        if (!oh) {
 200                pr_err("Could not look up %s\n", oh_name);
 201                r = -ENODEV;
 202                goto err;
 203        }
 204
 205        pdev = platform_device_alloc(pdev_name, pdev_id);
 206        if (!pdev) {
 207                pr_err("Could not create pdev for %s\n", pdev_name);
 208                r = -ENOMEM;
 209                goto err;
 210        }
 211
 212        if (parent != NULL)
 213                pdev->dev.parent = &parent->dev;
 214
 215        if (pdev->id != -1)
 216                dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
 217        else
 218                dev_set_name(&pdev->dev, "%s", pdev->name);
 219
 220        ohs[0] = oh;
 221        od = omap_device_alloc(pdev, ohs, 1, NULL, 0);
 222        if (!od) {
 223                pr_err("Could not alloc omap_device for %s\n", pdev_name);
 224                r = -ENOMEM;
 225                goto err;
 226        }
 227
 228        r = platform_device_add_data(pdev, pdata, pdata_len);
 229        if (r) {
 230                pr_err("Could not set pdata for %s\n", pdev_name);
 231                goto err;
 232        }
 233
 234        r = omap_device_register(pdev);
 235        if (r) {
 236                pr_err("Could not register omap_device for %s\n", pdev_name);
 237                goto err;
 238        }
 239
 240        return pdev;
 241
 242err:
 243        return ERR_PTR(r);
 244}
 245
 246static struct platform_device *create_simple_dss_pdev(const char *pdev_name,
 247                int pdev_id, void *pdata, int pdata_len,
 248                struct platform_device *parent)
 249{
 250        struct platform_device *pdev;
 251        int r;
 252
 253        pdev = platform_device_alloc(pdev_name, pdev_id);
 254        if (!pdev) {
 255                pr_err("Could not create pdev for %s\n", pdev_name);
 256                r = -ENOMEM;
 257                goto err;
 258        }
 259
 260        if (parent != NULL)
 261                pdev->dev.parent = &parent->dev;
 262
 263        if (pdev->id != -1)
 264                dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
 265        else
 266                dev_set_name(&pdev->dev, "%s", pdev->name);
 267
 268        r = platform_device_add_data(pdev, pdata, pdata_len);
 269        if (r) {
 270                pr_err("Could not set pdata for %s\n", pdev_name);
 271                goto err;
 272        }
 273
 274        r = platform_device_add(pdev);
 275        if (r) {
 276                pr_err("Could not register platform_device for %s\n", pdev_name);
 277                goto err;
 278        }
 279
 280        return pdev;
 281
 282err:
 283        return ERR_PTR(r);
 284}
 285
 286int __init omap_display_init(struct omap_dss_board_info *board_data)
 287{
 288        int r = 0;
 289        struct platform_device *pdev;
 290        int i, oh_count;
 291        const struct omap_dss_hwmod_data *curr_dss_hwmod;
 292        struct platform_device *dss_pdev;
 293
 294        /* create omapdss device */
 295
 296        board_data->dsi_enable_pads = omap_dsi_enable_pads;
 297        board_data->dsi_disable_pads = omap_dsi_disable_pads;
 298        board_data->get_context_loss_count = omap_pm_get_dev_context_loss_count;
 299        board_data->set_min_bus_tput = omap_dss_set_min_bus_tput;
 300
 301        omap_display_device.dev.platform_data = board_data;
 302
 303        r = platform_device_register(&omap_display_device);
 304        if (r < 0) {
 305                pr_err("Unable to register omapdss device\n");
 306                return r;
 307        }
 308
 309        /* create devices for dss hwmods */
 310
 311        if (cpu_is_omap24xx()) {
 312                curr_dss_hwmod = omap2_dss_hwmod_data;
 313                oh_count = ARRAY_SIZE(omap2_dss_hwmod_data);
 314        } else if (cpu_is_omap34xx()) {
 315                curr_dss_hwmod = omap3_dss_hwmod_data;
 316                oh_count = ARRAY_SIZE(omap3_dss_hwmod_data);
 317        } else {
 318                curr_dss_hwmod = omap4_dss_hwmod_data;
 319                oh_count = ARRAY_SIZE(omap4_dss_hwmod_data);
 320        }
 321
 322        /*
 323         * First create the pdev for dss_core, which is used as a parent device
 324         * by the other dss pdevs. Note: dss_core has to be the first item in
 325         * the hwmod list.
 326         */
 327        dss_pdev = create_dss_pdev(curr_dss_hwmod[0].dev_name,
 328                        curr_dss_hwmod[0].id,
 329                        curr_dss_hwmod[0].oh_name,
 330                        board_data, sizeof(*board_data),
 331                        NULL);
 332
 333        if (IS_ERR(dss_pdev)) {
 334                pr_err("Could not build omap_device for %s\n",
 335                                curr_dss_hwmod[0].oh_name);
 336
 337                return PTR_ERR(dss_pdev);
 338        }
 339
 340        for (i = 1; i < oh_count; i++) {
 341                pdev = create_dss_pdev(curr_dss_hwmod[i].dev_name,
 342                                curr_dss_hwmod[i].id,
 343                                curr_dss_hwmod[i].oh_name,
 344                                board_data, sizeof(*board_data),
 345                                dss_pdev);
 346
 347                if (IS_ERR(pdev)) {
 348                        pr_err("Could not build omap_device for %s\n",
 349                                        curr_dss_hwmod[i].oh_name);
 350
 351                        return PTR_ERR(pdev);
 352                }
 353        }
 354
 355        /* Create devices for DPI and SDI */
 356
 357        pdev = create_simple_dss_pdev("omapdss_dpi", -1,
 358                        board_data, sizeof(*board_data), dss_pdev);
 359        if (IS_ERR(pdev)) {
 360                pr_err("Could not build platform_device for omapdss_dpi\n");
 361                return PTR_ERR(pdev);
 362        }
 363
 364        if (cpu_is_omap34xx()) {
 365                pdev = create_simple_dss_pdev("omapdss_sdi", -1,
 366                                board_data, sizeof(*board_data), dss_pdev);
 367                if (IS_ERR(pdev)) {
 368                        pr_err("Could not build platform_device for omapdss_sdi\n");
 369                        return PTR_ERR(pdev);
 370                }
 371        }
 372
 373        return 0;
 374}
 375
 376static void dispc_disable_outputs(void)
 377{
 378        u32 v, irq_mask = 0;
 379        bool lcd_en, digit_en, lcd2_en = false;
 380        int i;
 381        struct omap_dss_dispc_dev_attr *da;
 382        struct omap_hwmod *oh;
 383
 384        oh = omap_hwmod_lookup("dss_dispc");
 385        if (!oh) {
 386                WARN(1, "display: could not disable outputs during reset - could not find dss_dispc hwmod\n");
 387                return;
 388        }
 389
 390        if (!oh->dev_attr) {
 391                pr_err("display: could not disable outputs during reset due to missing dev_attr\n");
 392                return;
 393        }
 394
 395        da = (struct omap_dss_dispc_dev_attr *)oh->dev_attr;
 396
 397        /* store value of LCDENABLE and DIGITENABLE bits */
 398        v = omap_hwmod_read(oh, DISPC_CONTROL);
 399        lcd_en = v & LCD_EN_MASK;
 400        digit_en = v & DIGIT_EN_MASK;
 401
 402        /* store value of LCDENABLE for LCD2 */
 403        if (da->manager_count > 2) {
 404                v = omap_hwmod_read(oh, DISPC_CONTROL2);
 405                lcd2_en = v & LCD_EN_MASK;
 406        }
 407
 408        if (!(lcd_en | digit_en | lcd2_en))
 409                return; /* no managers currently enabled */
 410
 411        /*
 412         * If any manager was enabled, we need to disable it before
 413         * DSS clocks are disabled or DISPC module is reset
 414         */
 415        if (lcd_en)
 416                irq_mask |= 1 << FRAMEDONE_IRQ_SHIFT;
 417
 418        if (digit_en) {
 419                if (da->has_framedonetv_irq) {
 420                        irq_mask |= 1 << FRAMEDONETV_IRQ_SHIFT;
 421                } else {
 422                        irq_mask |= 1 << EVSYNC_EVEN_IRQ_SHIFT |
 423                                1 << EVSYNC_ODD_IRQ_SHIFT;
 424                }
 425        }
 426
 427        if (lcd2_en)
 428                irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT;
 429
 430        /*
 431         * clear any previous FRAMEDONE, FRAMEDONETV,
 432         * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts
 433         */
 434        omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS);
 435
 436        /* disable LCD and TV managers */
 437        v = omap_hwmod_read(oh, DISPC_CONTROL);
 438        v &= ~(LCD_EN_MASK | DIGIT_EN_MASK);
 439        omap_hwmod_write(v, oh, DISPC_CONTROL);
 440
 441        /* disable LCD2 manager */
 442        if (da->manager_count > 2) {
 443                v = omap_hwmod_read(oh, DISPC_CONTROL2);
 444                v &= ~LCD_EN_MASK;
 445                omap_hwmod_write(v, oh, DISPC_CONTROL2);
 446        }
 447
 448        i = 0;
 449        while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) !=
 450               irq_mask) {
 451                i++;
 452                if (i > FRAMEDONE_IRQ_TIMEOUT) {
 453                        pr_err("didn't get FRAMEDONE1/2 or TV interrupt\n");
 454                        break;
 455                }
 456                mdelay(1);
 457        }
 458}
 459
 460#define MAX_MODULE_SOFTRESET_WAIT       10000
 461int omap_dss_reset(struct omap_hwmod *oh)
 462{
 463        struct omap_hwmod_opt_clk *oc;
 464        int c = 0;
 465        int i, r;
 466
 467        if (!(oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)) {
 468                pr_err("dss_core: hwmod data doesn't contain reset data\n");
 469                return -EINVAL;
 470        }
 471
 472        for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
 473                if (oc->_clk)
 474                        clk_enable(oc->_clk);
 475
 476        dispc_disable_outputs();
 477
 478        /* clear SDI registers */
 479        if (cpu_is_omap3430()) {
 480                omap_hwmod_write(0x0, oh, DSS_SDI_CONTROL);
 481                omap_hwmod_write(0x0, oh, DSS_PLL_CONTROL);
 482        }
 483
 484        /*
 485         * clear DSS_CONTROL register to switch DSS clock sources to
 486         * PRCM clock, if any
 487         */
 488        omap_hwmod_write(0x0, oh, DSS_CONTROL);
 489
 490        omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs)
 491                                & SYSS_RESETDONE_MASK),
 492                        MAX_MODULE_SOFTRESET_WAIT, c);
 493
 494        if (c == MAX_MODULE_SOFTRESET_WAIT)
 495                pr_warning("dss_core: waiting for reset to finish failed\n");
 496        else
 497                pr_debug("dss_core: softreset done\n");
 498
 499        for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
 500                if (oc->_clk)
 501                        clk_disable(oc->_clk);
 502
 503        r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0;
 504
 505        return r;
 506}
 507