linux/drivers/video/omap2/dss/dpi.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/omap2/dss/dpi.c
   3 *
   4 * Copyright (C) 2009 Nokia Corporation
   5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
   6 *
   7 * Some code and ideas taken from drivers/video/omap/ driver
   8 * by Imre Deak.
   9 *
  10 * This program is free software; you can redistribute it and/or modify it
  11 * under the terms of the GNU General Public License version 2 as published by
  12 * the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful, but WITHOUT
  15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  17 * more details.
  18 *
  19 * You should have received a copy of the GNU General Public License along with
  20 * this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#define DSS_SUBSYS_NAME "DPI"
  24
  25#include <linux/kernel.h>
  26#include <linux/delay.h>
  27#include <linux/export.h>
  28#include <linux/err.h>
  29#include <linux/errno.h>
  30#include <linux/platform_device.h>
  31#include <linux/regulator/consumer.h>
  32#include <linux/string.h>
  33
  34#include <video/omapdss.h>
  35
  36#include "dss.h"
  37#include "dss_features.h"
  38
  39static struct {
  40        struct regulator *vdds_dsi_reg;
  41        struct platform_device *dsidev;
  42
  43        struct mutex lock;
  44
  45        struct omap_video_timings timings;
  46        struct dss_lcd_mgr_config mgr_config;
  47        int data_lines;
  48
  49        struct omap_dss_output output;
  50} dpi;
  51
  52static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
  53{
  54        /*
  55         * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
  56         * would also be used for DISPC fclk. Meaning, when the DPI output is
  57         * disabled, DISPC clock will be disabled, and TV out will stop.
  58         */
  59        switch (omapdss_get_version()) {
  60        case OMAPDSS_VER_OMAP24xx:
  61        case OMAPDSS_VER_OMAP34xx_ES1:
  62        case OMAPDSS_VER_OMAP34xx_ES3:
  63        case OMAPDSS_VER_OMAP3630:
  64        case OMAPDSS_VER_AM35xx:
  65                return NULL;
  66        default:
  67                break;
  68        }
  69
  70        switch (channel) {
  71        case OMAP_DSS_CHANNEL_LCD:
  72                return dsi_get_dsidev_from_id(0);
  73        case OMAP_DSS_CHANNEL_LCD2:
  74                return dsi_get_dsidev_from_id(1);
  75        default:
  76                return NULL;
  77        }
  78}
  79
  80static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
  81{
  82        switch (channel) {
  83        case OMAP_DSS_CHANNEL_LCD:
  84                return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
  85        case OMAP_DSS_CHANNEL_LCD2:
  86                return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
  87        default:
  88                /* this shouldn't happen */
  89                WARN_ON(1);
  90                return OMAP_DSS_CLK_SRC_FCK;
  91        }
  92}
  93
  94static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
  95                unsigned long pck_req, unsigned long *fck, int *lck_div,
  96                int *pck_div)
  97{
  98        struct omap_overlay_manager *mgr = dssdev->output->manager;
  99        struct dsi_clock_info dsi_cinfo;
 100        struct dispc_clock_info dispc_cinfo;
 101        int r;
 102
 103        r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
 104                        &dispc_cinfo);
 105        if (r)
 106                return r;
 107
 108        r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
 109        if (r)
 110                return r;
 111
 112        dss_select_lcd_clk_source(mgr->id,
 113                        dpi_get_alt_clk_src(mgr->id));
 114
 115        dpi.mgr_config.clock_info = dispc_cinfo;
 116
 117        *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
 118        *lck_div = dispc_cinfo.lck_div;
 119        *pck_div = dispc_cinfo.pck_div;
 120
 121        return 0;
 122}
 123
 124static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
 125                unsigned long pck_req, unsigned long *fck, int *lck_div,
 126                int *pck_div)
 127{
 128        struct dss_clock_info dss_cinfo;
 129        struct dispc_clock_info dispc_cinfo;
 130        int r;
 131
 132        r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
 133        if (r)
 134                return r;
 135
 136        r = dss_set_clock_div(&dss_cinfo);
 137        if (r)
 138                return r;
 139
 140        dpi.mgr_config.clock_info = dispc_cinfo;
 141
 142        *fck = dss_cinfo.fck;
 143        *lck_div = dispc_cinfo.lck_div;
 144        *pck_div = dispc_cinfo.pck_div;
 145
 146        return 0;
 147}
 148
 149static int dpi_set_mode(struct omap_dss_device *dssdev)
 150{
 151        struct omap_video_timings *t = &dpi.timings;
 152        struct omap_overlay_manager *mgr = dssdev->output->manager;
 153        int lck_div = 0, pck_div = 0;
 154        unsigned long fck = 0;
 155        unsigned long pck;
 156        int r = 0;
 157
 158        if (dpi.dsidev)
 159                r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
 160                                &lck_div, &pck_div);
 161        else
 162                r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
 163                                &lck_div, &pck_div);
 164        if (r)
 165                return r;
 166
 167        pck = fck / lck_div / pck_div / 1000;
 168
 169        if (pck != t->pixel_clock) {
 170                DSSWARN("Could not find exact pixel clock. "
 171                                "Requested %d kHz, got %lu kHz\n",
 172                                t->pixel_clock, pck);
 173
 174                t->pixel_clock = pck;
 175        }
 176
 177        dss_mgr_set_timings(mgr, t);
 178
 179        return 0;
 180}
 181
 182static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
 183{
 184        struct omap_overlay_manager *mgr = dssdev->output->manager;
 185
 186        dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
 187
 188        dpi.mgr_config.stallmode = false;
 189        dpi.mgr_config.fifohandcheck = false;
 190
 191        dpi.mgr_config.video_port_width = dpi.data_lines;
 192
 193        dpi.mgr_config.lcden_sig_polarity = 0;
 194
 195        dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
 196}
 197
 198int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
 199{
 200        struct omap_dss_output *out = dssdev->output;
 201        int r;
 202
 203        mutex_lock(&dpi.lock);
 204
 205        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
 206                DSSERR("no VDSS_DSI regulator\n");
 207                r = -ENODEV;
 208                goto err_no_reg;
 209        }
 210
 211        if (out == NULL || out->manager == NULL) {
 212                DSSERR("failed to enable display: no output/manager\n");
 213                r = -ENODEV;
 214                goto err_no_out_mgr;
 215        }
 216
 217        r = omap_dss_start_device(dssdev);
 218        if (r) {
 219                DSSERR("failed to start device\n");
 220                goto err_start_dev;
 221        }
 222
 223        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
 224                r = regulator_enable(dpi.vdds_dsi_reg);
 225                if (r)
 226                        goto err_reg_enable;
 227        }
 228
 229        r = dispc_runtime_get();
 230        if (r)
 231                goto err_get_dispc;
 232
 233        r = dss_dpi_select_source(dssdev->channel);
 234        if (r)
 235                goto err_src_sel;
 236
 237        if (dpi.dsidev) {
 238                r = dsi_runtime_get(dpi.dsidev);
 239                if (r)
 240                        goto err_get_dsi;
 241
 242                r = dsi_pll_init(dpi.dsidev, 0, 1);
 243                if (r)
 244                        goto err_dsi_pll_init;
 245        }
 246
 247        r = dpi_set_mode(dssdev);
 248        if (r)
 249                goto err_set_mode;
 250
 251        dpi_config_lcd_manager(dssdev);
 252
 253        mdelay(2);
 254
 255        r = dss_mgr_enable(out->manager);
 256        if (r)
 257                goto err_mgr_enable;
 258
 259        mutex_unlock(&dpi.lock);
 260
 261        return 0;
 262
 263err_mgr_enable:
 264err_set_mode:
 265        if (dpi.dsidev)
 266                dsi_pll_uninit(dpi.dsidev, true);
 267err_dsi_pll_init:
 268        if (dpi.dsidev)
 269                dsi_runtime_put(dpi.dsidev);
 270err_get_dsi:
 271err_src_sel:
 272        dispc_runtime_put();
 273err_get_dispc:
 274        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 275                regulator_disable(dpi.vdds_dsi_reg);
 276err_reg_enable:
 277        omap_dss_stop_device(dssdev);
 278err_start_dev:
 279err_no_out_mgr:
 280err_no_reg:
 281        mutex_unlock(&dpi.lock);
 282        return r;
 283}
 284EXPORT_SYMBOL(omapdss_dpi_display_enable);
 285
 286void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
 287{
 288        struct omap_overlay_manager *mgr = dssdev->output->manager;
 289
 290        mutex_lock(&dpi.lock);
 291
 292        dss_mgr_disable(mgr);
 293
 294        if (dpi.dsidev) {
 295                dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
 296                dsi_pll_uninit(dpi.dsidev, true);
 297                dsi_runtime_put(dpi.dsidev);
 298        }
 299
 300        dispc_runtime_put();
 301
 302        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 303                regulator_disable(dpi.vdds_dsi_reg);
 304
 305        omap_dss_stop_device(dssdev);
 306
 307        mutex_unlock(&dpi.lock);
 308}
 309EXPORT_SYMBOL(omapdss_dpi_display_disable);
 310
 311void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
 312                struct omap_video_timings *timings)
 313{
 314        DSSDBG("dpi_set_timings\n");
 315
 316        mutex_lock(&dpi.lock);
 317
 318        dpi.timings = *timings;
 319
 320        mutex_unlock(&dpi.lock);
 321}
 322EXPORT_SYMBOL(omapdss_dpi_set_timings);
 323
 324int dpi_check_timings(struct omap_dss_device *dssdev,
 325                        struct omap_video_timings *timings)
 326{
 327        int r;
 328        struct omap_overlay_manager *mgr = dssdev->output->manager;
 329        int lck_div, pck_div;
 330        unsigned long fck;
 331        unsigned long pck;
 332        struct dispc_clock_info dispc_cinfo;
 333
 334        if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
 335                return -EINVAL;
 336
 337        if (timings->pixel_clock == 0)
 338                return -EINVAL;
 339
 340        if (dpi.dsidev) {
 341                struct dsi_clock_info dsi_cinfo;
 342                r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
 343                                timings->pixel_clock * 1000,
 344                                &dsi_cinfo, &dispc_cinfo);
 345
 346                if (r)
 347                        return r;
 348
 349                fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
 350        } else {
 351                struct dss_clock_info dss_cinfo;
 352                r = dss_calc_clock_div(timings->pixel_clock * 1000,
 353                                &dss_cinfo, &dispc_cinfo);
 354
 355                if (r)
 356                        return r;
 357
 358                fck = dss_cinfo.fck;
 359        }
 360
 361        lck_div = dispc_cinfo.lck_div;
 362        pck_div = dispc_cinfo.pck_div;
 363
 364        pck = fck / lck_div / pck_div / 1000;
 365
 366        timings->pixel_clock = pck;
 367
 368        return 0;
 369}
 370EXPORT_SYMBOL(dpi_check_timings);
 371
 372void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
 373{
 374        mutex_lock(&dpi.lock);
 375
 376        dpi.data_lines = data_lines;
 377
 378        mutex_unlock(&dpi.lock);
 379}
 380EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
 381
 382static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
 383{
 384        int r;
 385
 386        /* do initial setup with the PLL to see if it is operational */
 387
 388        r = dsi_runtime_get(dsidev);
 389        if (r)
 390                return r;
 391
 392        r = dsi_pll_init(dsidev, 0, 1);
 393        if (r) {
 394                dsi_runtime_put(dsidev);
 395                return r;
 396        }
 397
 398        dsi_pll_uninit(dsidev, true);
 399        dsi_runtime_put(dsidev);
 400
 401        return 0;
 402}
 403
 404static int __init dpi_init_display(struct omap_dss_device *dssdev)
 405{
 406        struct platform_device *dsidev;
 407
 408        DSSDBG("init_display\n");
 409
 410        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
 411                                        dpi.vdds_dsi_reg == NULL) {
 412                struct regulator *vdds_dsi;
 413
 414                vdds_dsi = dss_get_vdds_dsi();
 415
 416                if (IS_ERR(vdds_dsi)) {
 417                        DSSERR("can't get VDDS_DSI regulator\n");
 418                        return PTR_ERR(vdds_dsi);
 419                }
 420
 421                dpi.vdds_dsi_reg = vdds_dsi;
 422        }
 423
 424        /*
 425         * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
 426         * source for DPI is SoC integration detail, not something that should
 427         * be configured in the dssdev
 428         */
 429        dsidev = dpi_get_dsidev(dssdev->channel);
 430
 431        if (dsidev && dpi_verify_dsi_pll(dsidev)) {
 432                dsidev = NULL;
 433                DSSWARN("DSI PLL not operational\n");
 434        }
 435
 436        if (dsidev)
 437                DSSDBG("using DSI PLL for DPI clock\n");
 438
 439        dpi.dsidev = dsidev;
 440
 441        return 0;
 442}
 443
 444static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
 445{
 446        struct omap_dss_board_info *pdata = pdev->dev.platform_data;
 447        const char *def_disp_name = omapdss_get_default_display_name();
 448        struct omap_dss_device *def_dssdev;
 449        int i;
 450
 451        def_dssdev = NULL;
 452
 453        for (i = 0; i < pdata->num_devices; ++i) {
 454                struct omap_dss_device *dssdev = pdata->devices[i];
 455
 456                if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
 457                        continue;
 458
 459                if (def_dssdev == NULL)
 460                        def_dssdev = dssdev;
 461
 462                if (def_disp_name != NULL &&
 463                                strcmp(dssdev->name, def_disp_name) == 0) {
 464                        def_dssdev = dssdev;
 465                        break;
 466                }
 467        }
 468
 469        return def_dssdev;
 470}
 471
 472static void __init dpi_probe_pdata(struct platform_device *dpidev)
 473{
 474        struct omap_dss_device *plat_dssdev;
 475        struct omap_dss_device *dssdev;
 476        int r;
 477
 478        plat_dssdev = dpi_find_dssdev(dpidev);
 479
 480        if (!plat_dssdev)
 481                return;
 482
 483        dssdev = dss_alloc_and_init_device(&dpidev->dev);
 484        if (!dssdev)
 485                return;
 486
 487        dss_copy_device_pdata(dssdev, plat_dssdev);
 488
 489        r = dpi_init_display(dssdev);
 490        if (r) {
 491                DSSERR("device %s init failed: %d\n", dssdev->name, r);
 492                dss_put_device(dssdev);
 493                return;
 494        }
 495
 496        r = omapdss_output_set_device(&dpi.output, dssdev);
 497        if (r) {
 498                DSSERR("failed to connect output to new device: %s\n",
 499                                dssdev->name);
 500                dss_put_device(dssdev);
 501                return;
 502        }
 503
 504        r = dss_add_device(dssdev);
 505        if (r) {
 506                DSSERR("device %s register failed: %d\n", dssdev->name, r);
 507                omapdss_output_unset_device(&dpi.output);
 508                dss_put_device(dssdev);
 509                return;
 510        }
 511}
 512
 513static void __init dpi_init_output(struct platform_device *pdev)
 514{
 515        struct omap_dss_output *out = &dpi.output;
 516
 517        out->pdev = pdev;
 518        out->id = OMAP_DSS_OUTPUT_DPI;
 519        out->type = OMAP_DISPLAY_TYPE_DPI;
 520
 521        dss_register_output(out);
 522}
 523
 524static void __exit dpi_uninit_output(struct platform_device *pdev)
 525{
 526        struct omap_dss_output *out = &dpi.output;
 527
 528        dss_unregister_output(out);
 529}
 530
 531static int __init omap_dpi_probe(struct platform_device *pdev)
 532{
 533        mutex_init(&dpi.lock);
 534
 535        dpi_init_output(pdev);
 536
 537        dpi_probe_pdata(pdev);
 538
 539        return 0;
 540}
 541
 542static int __exit omap_dpi_remove(struct platform_device *pdev)
 543{
 544        dss_unregister_child_devices(&pdev->dev);
 545
 546        dpi_uninit_output(pdev);
 547
 548        return 0;
 549}
 550
 551static struct platform_driver omap_dpi_driver = {
 552        .remove         = __exit_p(omap_dpi_remove),
 553        .driver         = {
 554                .name   = "omapdss_dpi",
 555                .owner  = THIS_MODULE,
 556        },
 557};
 558
 559int __init dpi_init_platform_driver(void)
 560{
 561        return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe);
 562}
 563
 564void __exit dpi_uninit_platform_driver(void)
 565{
 566        platform_driver_unregister(&omap_dpi_driver);
 567}
 568
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.