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_dss_clk_source clk)
  53{
  54        int dsi_module;
  55
  56        dsi_module = clk == OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ? 0 : 1;
  57
  58        return dsi_get_dsidev_from_id(dsi_module);
  59}
  60
  61static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
  62{
  63        if (dssdev->clocks.dispc.dispc_fclk_src ==
  64                        OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
  65                        dssdev->clocks.dispc.dispc_fclk_src ==
  66                        OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC ||
  67                        dssdev->clocks.dispc.channel.lcd_clk_src ==
  68                        OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
  69                        dssdev->clocks.dispc.channel.lcd_clk_src ==
  70                        OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC)
  71                return true;
  72        else
  73                return false;
  74}
  75
  76static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
  77                unsigned long pck_req, unsigned long *fck, int *lck_div,
  78                int *pck_div)
  79{
  80        struct dsi_clock_info dsi_cinfo;
  81        struct dispc_clock_info dispc_cinfo;
  82        int r;
  83
  84        r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
  85                        &dispc_cinfo);
  86        if (r)
  87                return r;
  88
  89        r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
  90        if (r)
  91                return r;
  92
  93        dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
  94
  95        dpi.mgr_config.clock_info = dispc_cinfo;
  96
  97        *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
  98        *lck_div = dispc_cinfo.lck_div;
  99        *pck_div = dispc_cinfo.pck_div;
 100
 101        return 0;
 102}
 103
 104static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
 105                unsigned long pck_req, unsigned long *fck, int *lck_div,
 106                int *pck_div)
 107{
 108        struct dss_clock_info dss_cinfo;
 109        struct dispc_clock_info dispc_cinfo;
 110        int r;
 111
 112        r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
 113        if (r)
 114                return r;
 115
 116        r = dss_set_clock_div(&dss_cinfo);
 117        if (r)
 118                return r;
 119
 120        dpi.mgr_config.clock_info = dispc_cinfo;
 121
 122        *fck = dss_cinfo.fck;
 123        *lck_div = dispc_cinfo.lck_div;
 124        *pck_div = dispc_cinfo.pck_div;
 125
 126        return 0;
 127}
 128
 129static int dpi_set_mode(struct omap_dss_device *dssdev)
 130{
 131        struct omap_video_timings *t = &dpi.timings;
 132        struct omap_overlay_manager *mgr = dssdev->output->manager;
 133        int lck_div = 0, pck_div = 0;
 134        unsigned long fck = 0;
 135        unsigned long pck;
 136        int r = 0;
 137
 138        if (dpi_use_dsi_pll(dssdev))
 139                r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
 140                                &lck_div, &pck_div);
 141        else
 142                r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
 143                                &lck_div, &pck_div);
 144        if (r)
 145                return r;
 146
 147        pck = fck / lck_div / pck_div / 1000;
 148
 149        if (pck != t->pixel_clock) {
 150                DSSWARN("Could not find exact pixel clock. "
 151                                "Requested %d kHz, got %lu kHz\n",
 152                                t->pixel_clock, pck);
 153
 154                t->pixel_clock = pck;
 155        }
 156
 157        dss_mgr_set_timings(mgr, t);
 158
 159        return 0;
 160}
 161
 162static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
 163{
 164        struct omap_overlay_manager *mgr = dssdev->output->manager;
 165
 166        dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
 167
 168        dpi.mgr_config.stallmode = false;
 169        dpi.mgr_config.fifohandcheck = false;
 170
 171        dpi.mgr_config.video_port_width = dpi.data_lines;
 172
 173        dpi.mgr_config.lcden_sig_polarity = 0;
 174
 175        dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
 176}
 177
 178int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
 179{
 180        struct omap_dss_output *out = dssdev->output;
 181        int r;
 182
 183        mutex_lock(&dpi.lock);
 184
 185        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
 186                DSSERR("no VDSS_DSI regulator\n");
 187                r = -ENODEV;
 188                goto err_no_reg;
 189        }
 190
 191        if (out == NULL || out->manager == NULL) {
 192                DSSERR("failed to enable display: no output/manager\n");
 193                r = -ENODEV;
 194                goto err_no_out_mgr;
 195        }
 196
 197        r = omap_dss_start_device(dssdev);
 198        if (r) {
 199                DSSERR("failed to start device\n");
 200                goto err_start_dev;
 201        }
 202
 203        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
 204                r = regulator_enable(dpi.vdds_dsi_reg);
 205                if (r)
 206                        goto err_reg_enable;
 207        }
 208
 209        r = dispc_runtime_get();
 210        if (r)
 211                goto err_get_dispc;
 212
 213        r = dss_dpi_select_source(dssdev->channel);
 214        if (r)
 215                goto err_src_sel;
 216
 217        if (dpi_use_dsi_pll(dssdev)) {
 218                r = dsi_runtime_get(dpi.dsidev);
 219                if (r)
 220                        goto err_get_dsi;
 221
 222                r = dsi_pll_init(dpi.dsidev, 0, 1);
 223                if (r)
 224                        goto err_dsi_pll_init;
 225        }
 226
 227        r = dpi_set_mode(dssdev);
 228        if (r)
 229                goto err_set_mode;
 230
 231        dpi_config_lcd_manager(dssdev);
 232
 233        mdelay(2);
 234
 235        r = dss_mgr_enable(out->manager);
 236        if (r)
 237                goto err_mgr_enable;
 238
 239        mutex_unlock(&dpi.lock);
 240
 241        return 0;
 242
 243err_mgr_enable:
 244err_set_mode:
 245        if (dpi_use_dsi_pll(dssdev))
 246                dsi_pll_uninit(dpi.dsidev, true);
 247err_dsi_pll_init:
 248        if (dpi_use_dsi_pll(dssdev))
 249                dsi_runtime_put(dpi.dsidev);
 250err_get_dsi:
 251err_src_sel:
 252        dispc_runtime_put();
 253err_get_dispc:
 254        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 255                regulator_disable(dpi.vdds_dsi_reg);
 256err_reg_enable:
 257        omap_dss_stop_device(dssdev);
 258err_start_dev:
 259err_no_out_mgr:
 260err_no_reg:
 261        mutex_unlock(&dpi.lock);
 262        return r;
 263}
 264EXPORT_SYMBOL(omapdss_dpi_display_enable);
 265
 266void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
 267{
 268        struct omap_overlay_manager *mgr = dssdev->output->manager;
 269
 270        mutex_lock(&dpi.lock);
 271
 272        dss_mgr_disable(mgr);
 273
 274        if (dpi_use_dsi_pll(dssdev)) {
 275                dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
 276                dsi_pll_uninit(dpi.dsidev, true);
 277                dsi_runtime_put(dpi.dsidev);
 278        }
 279
 280        dispc_runtime_put();
 281
 282        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
 283                regulator_disable(dpi.vdds_dsi_reg);
 284
 285        omap_dss_stop_device(dssdev);
 286
 287        mutex_unlock(&dpi.lock);
 288}
 289EXPORT_SYMBOL(omapdss_dpi_display_disable);
 290
 291void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
 292                struct omap_video_timings *timings)
 293{
 294        DSSDBG("dpi_set_timings\n");
 295
 296        mutex_lock(&dpi.lock);
 297
 298        dpi.timings = *timings;
 299
 300        mutex_unlock(&dpi.lock);
 301}
 302EXPORT_SYMBOL(omapdss_dpi_set_timings);
 303
 304int dpi_check_timings(struct omap_dss_device *dssdev,
 305                        struct omap_video_timings *timings)
 306{
 307        int r;
 308        struct omap_overlay_manager *mgr = dssdev->output->manager;
 309        int lck_div, pck_div;
 310        unsigned long fck;
 311        unsigned long pck;
 312        struct dispc_clock_info dispc_cinfo;
 313
 314        if (dss_mgr_check_timings(mgr, timings))
 315                return -EINVAL;
 316
 317        if (timings->pixel_clock == 0)
 318                return -EINVAL;
 319
 320        if (dpi_use_dsi_pll(dssdev)) {
 321                struct dsi_clock_info dsi_cinfo;
 322                r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
 323                                timings->pixel_clock * 1000,
 324                                &dsi_cinfo, &dispc_cinfo);
 325
 326                if (r)
 327                        return r;
 328
 329                fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
 330        } else {
 331                struct dss_clock_info dss_cinfo;
 332                r = dss_calc_clock_div(timings->pixel_clock * 1000,
 333                                &dss_cinfo, &dispc_cinfo);
 334
 335                if (r)
 336                        return r;
 337
 338                fck = dss_cinfo.fck;
 339        }
 340
 341        lck_div = dispc_cinfo.lck_div;
 342        pck_div = dispc_cinfo.pck_div;
 343
 344        pck = fck / lck_div / pck_div / 1000;
 345
 346        timings->pixel_clock = pck;
 347
 348        return 0;
 349}
 350EXPORT_SYMBOL(dpi_check_timings);
 351
 352void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
 353{
 354        mutex_lock(&dpi.lock);
 355
 356        dpi.data_lines = data_lines;
 357
 358        mutex_unlock(&dpi.lock);
 359}
 360EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
 361
 362static int __init dpi_init_display(struct omap_dss_device *dssdev)
 363{
 364        DSSDBG("init_display\n");
 365
 366        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
 367                                        dpi.vdds_dsi_reg == NULL) {
 368                struct regulator *vdds_dsi;
 369
 370                vdds_dsi = dss_get_vdds_dsi();
 371
 372                if (IS_ERR(vdds_dsi)) {
 373                        DSSERR("can't get VDDS_DSI regulator\n");
 374                        return PTR_ERR(vdds_dsi);
 375                }
 376
 377                dpi.vdds_dsi_reg = vdds_dsi;
 378        }
 379
 380        if (dpi_use_dsi_pll(dssdev)) {
 381                enum omap_dss_clk_source dispc_fclk_src =
 382                        dssdev->clocks.dispc.dispc_fclk_src;
 383                dpi.dsidev = dpi_get_dsidev(dispc_fclk_src);
 384        }
 385
 386        return 0;
 387}
 388
 389static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
 390{
 391        struct omap_dss_board_info *pdata = pdev->dev.platform_data;
 392        const char *def_disp_name = dss_get_default_display_name();
 393        struct omap_dss_device *def_dssdev;
 394        int i;
 395
 396        def_dssdev = NULL;
 397
 398        for (i = 0; i < pdata->num_devices; ++i) {
 399                struct omap_dss_device *dssdev = pdata->devices[i];
 400
 401                if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
 402                        continue;
 403
 404                if (def_dssdev == NULL)
 405                        def_dssdev = dssdev;
 406
 407                if (def_disp_name != NULL &&
 408                                strcmp(dssdev->name, def_disp_name) == 0) {
 409                        def_dssdev = dssdev;
 410                        break;
 411                }
 412        }
 413
 414        return def_dssdev;
 415}
 416
 417static void __init dpi_probe_pdata(struct platform_device *dpidev)
 418{
 419        struct omap_dss_device *plat_dssdev;
 420        struct omap_dss_device *dssdev;
 421        int r;
 422
 423        plat_dssdev = dpi_find_dssdev(dpidev);
 424
 425        if (!plat_dssdev)
 426                return;
 427
 428        dssdev = dss_alloc_and_init_device(&dpidev->dev);
 429        if (!dssdev)
 430                return;
 431
 432        dss_copy_device_pdata(dssdev, plat_dssdev);
 433
 434        r = dpi_init_display(dssdev);
 435        if (r) {
 436                DSSERR("device %s init failed: %d\n", dssdev->name, r);
 437                dss_put_device(dssdev);
 438                return;
 439        }
 440
 441        r = dss_add_device(dssdev);
 442        if (r) {
 443                DSSERR("device %s register failed: %d\n", dssdev->name, r);
 444                dss_put_device(dssdev);
 445                return;
 446        }
 447}
 448
 449static void __init dpi_init_output(struct platform_device *pdev)
 450{
 451        struct omap_dss_output *out = &dpi.output;
 452
 453        out->pdev = pdev;
 454        out->id = OMAP_DSS_OUTPUT_DPI;
 455        out->type = OMAP_DISPLAY_TYPE_DPI;
 456
 457        dss_register_output(out);
 458}
 459
 460static void __exit dpi_uninit_output(struct platform_device *pdev)
 461{
 462        struct omap_dss_output *out = &dpi.output;
 463
 464        dss_unregister_output(out);
 465}
 466
 467static int __init omap_dpi_probe(struct platform_device *pdev)
 468{
 469        mutex_init(&dpi.lock);
 470
 471        dpi_init_output(pdev);
 472
 473        dpi_probe_pdata(pdev);
 474
 475        return 0;
 476}
 477
 478static int __exit omap_dpi_remove(struct platform_device *pdev)
 479{
 480        dss_unregister_child_devices(&pdev->dev);
 481
 482        dpi_uninit_output(pdev);
 483
 484        return 0;
 485}
 486
 487static struct platform_driver omap_dpi_driver = {
 488        .remove         = __exit_p(omap_dpi_remove),
 489        .driver         = {
 490                .name   = "omapdss_dpi",
 491                .owner  = THIS_MODULE,
 492        },
 493};
 494
 495int __init dpi_init_platform_driver(void)
 496{
 497        return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe);
 498}
 499
 500void __exit dpi_uninit_platform_driver(void)
 501{
 502        platform_driver_unregister(&omap_dpi_driver);
 503}
 504