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