linux/drivers/gpu/drm/gma500/mdfld_dsi_output.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2010 Intel Corporation
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the next
  12 * paragraph) shall be included in all copies or substantial portions of the
  13 * Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21 * DEALINGS IN THE SOFTWARE.
  22 *
  23 * Authors:
  24 * jim liu <jim.liu@intel.com>
  25 * Jackie Li<yaodong.li@intel.com>
  26 */
  27
  28#include <linux/module.h>
  29
  30#include "mdfld_dsi_output.h"
  31#include "mdfld_dsi_dpi.h"
  32#include "mdfld_output.h"
  33#include "mdfld_dsi_pkg_sender.h"
  34#include "tc35876x-dsi-lvds.h"
  35#include <linux/pm_runtime.h>
  36#include <asm/intel_scu_ipc.h>
  37
  38/* get the LABC from command line. */
  39static int LABC_control = 1;
  40
  41#ifdef MODULE
  42module_param(LABC_control, int, 0644);
  43#else
  44
  45static int __init parse_LABC_control(char *arg)
  46{
  47        /* LABC control can be passed in as a cmdline parameter */
  48        /* to enable this feature add LABC=1 to cmdline */
  49        /* to disable this feature add LABC=0 to cmdline */
  50        if (!arg)
  51                return -EINVAL;
  52
  53        if (!strcasecmp(arg, "0"))
  54                LABC_control = 0;
  55        else if (!strcasecmp(arg, "1"))
  56                LABC_control = 1;
  57
  58        return 0;
  59}
  60early_param("LABC", parse_LABC_control);
  61#endif
  62
  63/**
  64 * Check and see if the generic control or data buffer is empty and ready.
  65 */
  66void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg,
  67                                                        u32 fifo_stat)
  68{
  69        u32 GEN_BF_time_out_count;
  70
  71        /* Check MIPI Adatper command registers */
  72        for (GEN_BF_time_out_count = 0;
  73                        GEN_BF_time_out_count < GEN_FB_TIME_OUT;
  74                        GEN_BF_time_out_count++) {
  75                if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat)
  76                        break;
  77                udelay(100);
  78        }
  79
  80        if (GEN_BF_time_out_count == GEN_FB_TIME_OUT)
  81                DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n",
  82                                        gen_fifo_stat_reg);
  83}
  84
  85/**
  86 * Manage the DSI MIPI keyboard and display brightness.
  87 * FIXME: this is exported to OSPM code. should work out an specific
  88 * display interface to OSPM.
  89 */
  90
  91void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
  92{
  93        struct mdfld_dsi_pkg_sender *sender =
  94                                mdfld_dsi_get_pkg_sender(dsi_config);
  95        struct drm_device *dev;
  96        struct drm_psb_private *dev_priv;
  97        u32 gen_ctrl_val;
  98
  99        if (!sender) {
 100                DRM_ERROR("No sender found\n");
 101                return;
 102        }
 103
 104        dev = sender->dev;
 105        dev_priv = dev->dev_private;
 106
 107        /* Set default display backlight value to 85% (0xd8)*/
 108        mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
 109                                true);
 110
 111        /* Set minimum brightness setting of CABC function to 20% (0x33)*/
 112        mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
 113
 114        /* Enable backlight or/and LABC */
 115        gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
 116                                                                BACKLIGHT_ON;
 117        if (LABC_control == 1)
 118                gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
 119                                                                | GAMMA_AUTO;
 120
 121        if (LABC_control == 1)
 122                gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
 123
 124        dev_priv->mipi_ctrl_display = gen_ctrl_val;
 125
 126        mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
 127                                1, true);
 128
 129        mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
 130}
 131
 132void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
 133{
 134        struct mdfld_dsi_pkg_sender *sender;
 135        struct drm_psb_private *dev_priv;
 136        struct mdfld_dsi_config *dsi_config;
 137        u32 gen_ctrl_val = 0;
 138        int p_type = TMD_VID;
 139
 140        if (!dev || (pipe != 0 && pipe != 2)) {
 141                DRM_ERROR("Invalid parameter\n");
 142                return;
 143        }
 144
 145        p_type = mdfld_get_panel_type(dev, 0);
 146
 147        dev_priv = dev->dev_private;
 148
 149        if (pipe)
 150                dsi_config = dev_priv->dsi_configs[1];
 151        else
 152                dsi_config = dev_priv->dsi_configs[0];
 153
 154        sender = mdfld_dsi_get_pkg_sender(dsi_config);
 155
 156        if (!sender) {
 157                DRM_ERROR("No sender found\n");
 158                return;
 159        }
 160
 161        gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
 162
 163        dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
 164                                                        pipe, gen_ctrl_val);
 165
 166        if (p_type == TMD_VID) {
 167                /* Set display backlight value */
 168                mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
 169                                        (u8)gen_ctrl_val, 1, true);
 170        } else {
 171                /* Set display backlight value */
 172                mdfld_dsi_send_mcs_short(sender, write_display_brightness,
 173                                        (u8)gen_ctrl_val, 1, true);
 174
 175                /* Enable backlight control */
 176                if (level == 0)
 177                        gen_ctrl_val = 0;
 178                else
 179                        gen_ctrl_val = dev_priv->mipi_ctrl_display;
 180
 181                mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
 182                                        (u8)gen_ctrl_val, 1, true);
 183        }
 184}
 185
 186static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
 187                                u8 dcs, u32 *data, bool hs)
 188{
 189        struct mdfld_dsi_pkg_sender *sender
 190                = mdfld_dsi_get_pkg_sender(dsi_config);
 191
 192        if (!sender || !data) {
 193                DRM_ERROR("Invalid parameter\n");
 194                return -EINVAL;
 195        }
 196
 197        return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
 198}
 199
 200int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
 201                        bool hs)
 202{
 203        if (!dsi_config || !mode) {
 204                DRM_ERROR("Invalid parameter\n");
 205                return -EINVAL;
 206        }
 207
 208        return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
 209}
 210
 211/*
 212 * NOTE: this function was used by OSPM.
 213 * TODO: will be removed later, should work out display interfaces for OSPM
 214 */
 215void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
 216{
 217        if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
 218                DRM_ERROR("Invalid parameters\n");
 219                return;
 220        }
 221
 222        mdfld_dsi_dpi_controller_init(dsi_config, pipe);
 223}
 224
 225static void mdfld_dsi_connector_save(struct drm_connector *connector)
 226{
 227}
 228
 229static void mdfld_dsi_connector_restore(struct drm_connector *connector)
 230{
 231}
 232
 233/* FIXME: start using the force parameter */
 234static enum drm_connector_status
 235mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
 236{
 237        struct mdfld_dsi_connector *dsi_connector
 238                = mdfld_dsi_connector(connector);
 239
 240        dsi_connector->status = connector_status_connected;
 241
 242        return dsi_connector->status;
 243}
 244
 245static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
 246                                struct drm_property *property,
 247                                uint64_t value)
 248{
 249        struct drm_encoder *encoder = connector->encoder;
 250
 251        if (!strcmp(property->name, "scaling mode") && encoder) {
 252                struct psb_intel_crtc *psb_crtc =
 253                                        to_psb_intel_crtc(encoder->crtc);
 254                bool centerechange;
 255                uint64_t val;
 256
 257                if (!psb_crtc)
 258                        goto set_prop_error;
 259
 260                switch (value) {
 261                case DRM_MODE_SCALE_FULLSCREEN:
 262                        break;
 263                case DRM_MODE_SCALE_NO_SCALE:
 264                        break;
 265                case DRM_MODE_SCALE_ASPECT:
 266                        break;
 267                default:
 268                        goto set_prop_error;
 269                }
 270
 271                if (drm_object_property_get_value(&connector->base, property, &val))
 272                        goto set_prop_error;
 273
 274                if (val == value)
 275                        goto set_prop_done;
 276
 277                if (drm_object_property_set_value(&connector->base,
 278                                                        property, value))
 279                        goto set_prop_error;
 280
 281                centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
 282                        (value == DRM_MODE_SCALE_NO_SCALE);
 283
 284                if (psb_crtc->saved_mode.hdisplay != 0 &&
 285                    psb_crtc->saved_mode.vdisplay != 0) {
 286                        if (centerechange) {
 287                                if (!drm_crtc_helper_set_mode(encoder->crtc,
 288                                                &psb_crtc->saved_mode,
 289                                                encoder->crtc->x,
 290                                                encoder->crtc->y,
 291                                                encoder->crtc->fb))
 292                                        goto set_prop_error;
 293                        } else {
 294                                struct drm_encoder_helper_funcs *funcs =
 295                                                encoder->helper_private;
 296                                funcs->mode_set(encoder,
 297                                        &psb_crtc->saved_mode,
 298                                        &psb_crtc->saved_adjusted_mode);
 299                        }
 300                }
 301        } else if (!strcmp(property->name, "backlight") && encoder) {
 302                if (drm_object_property_set_value(&connector->base, property,
 303                                                                        value))
 304                        goto set_prop_error;
 305                else
 306                        gma_backlight_set(encoder->dev, value);
 307        }
 308set_prop_done:
 309        return 0;
 310set_prop_error:
 311        return -1;
 312}
 313
 314static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
 315{
 316        struct mdfld_dsi_connector *dsi_connector =
 317                                        mdfld_dsi_connector(connector);
 318        struct mdfld_dsi_pkg_sender *sender;
 319
 320        if (!dsi_connector)
 321                return;
 322        drm_sysfs_connector_remove(connector);
 323        drm_connector_cleanup(connector);
 324        sender = dsi_connector->pkg_sender;
 325        mdfld_dsi_pkg_sender_destroy(sender);
 326        kfree(dsi_connector);
 327}
 328
 329static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
 330{
 331        struct mdfld_dsi_connector *dsi_connector =
 332                                mdfld_dsi_connector(connector);
 333        struct mdfld_dsi_config *dsi_config =
 334                                mdfld_dsi_get_config(dsi_connector);
 335        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 336        struct drm_display_mode *dup_mode = NULL;
 337        struct drm_device *dev = connector->dev;
 338
 339        connector->display_info.min_vfreq = 0;
 340        connector->display_info.max_vfreq = 200;
 341        connector->display_info.min_hfreq = 0;
 342        connector->display_info.max_hfreq = 200;
 343
 344        if (fixed_mode) {
 345                dev_dbg(dev->dev, "fixed_mode %dx%d\n",
 346                                fixed_mode->hdisplay, fixed_mode->vdisplay);
 347                dup_mode = drm_mode_duplicate(dev, fixed_mode);
 348                drm_mode_probed_add(connector, dup_mode);
 349                return 1;
 350        }
 351        DRM_ERROR("Didn't get any modes!\n");
 352        return 0;
 353}
 354
 355static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
 356                                                struct drm_display_mode *mode)
 357{
 358        struct mdfld_dsi_connector *dsi_connector =
 359                                        mdfld_dsi_connector(connector);
 360        struct mdfld_dsi_config *dsi_config =
 361                                        mdfld_dsi_get_config(dsi_connector);
 362        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 363
 364        if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
 365                return MODE_NO_DBLESCAN;
 366
 367        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 368                return MODE_NO_INTERLACE;
 369
 370        /**
 371         * FIXME: current DC has no fitting unit, reject any mode setting
 372         * request
 373         * Will figure out a way to do up-scaling(pannel fitting) later.
 374         **/
 375        if (fixed_mode) {
 376                if (mode->hdisplay != fixed_mode->hdisplay)
 377                        return MODE_PANEL;
 378
 379                if (mode->vdisplay != fixed_mode->vdisplay)
 380                        return MODE_PANEL;
 381        }
 382
 383        return MODE_OK;
 384}
 385
 386static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
 387{
 388        if (mode == connector->dpms)
 389                return;
 390
 391        /*first, execute dpms*/
 392
 393        drm_helper_connector_dpms(connector, mode);
 394}
 395
 396static struct drm_encoder *mdfld_dsi_connector_best_encoder(
 397                                struct drm_connector *connector)
 398{
 399        struct mdfld_dsi_connector *dsi_connector =
 400                                mdfld_dsi_connector(connector);
 401        struct mdfld_dsi_config *dsi_config =
 402                                mdfld_dsi_get_config(dsi_connector);
 403        return &dsi_config->encoder->base.base;
 404}
 405
 406/*DSI connector funcs*/
 407static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
 408        .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
 409        .save = mdfld_dsi_connector_save,
 410        .restore = mdfld_dsi_connector_restore,
 411        .detect = mdfld_dsi_connector_detect,
 412        .fill_modes = drm_helper_probe_single_connector_modes,
 413        .set_property = mdfld_dsi_connector_set_property,
 414        .destroy = mdfld_dsi_connector_destroy,
 415};
 416
 417/*DSI connector helper funcs*/
 418static const struct drm_connector_helper_funcs
 419        mdfld_dsi_connector_helper_funcs = {
 420        .get_modes = mdfld_dsi_connector_get_modes,
 421        .mode_valid = mdfld_dsi_connector_mode_valid,
 422        .best_encoder = mdfld_dsi_connector_best_encoder,
 423};
 424
 425static int mdfld_dsi_get_default_config(struct drm_device *dev,
 426                                struct mdfld_dsi_config *config, int pipe)
 427{
 428        if (!dev || !config) {
 429                DRM_ERROR("Invalid parameters");
 430                return -EINVAL;
 431        }
 432
 433        config->bpp = 24;
 434        if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 435                config->lane_count = 4;
 436        else
 437                config->lane_count = 2;
 438        config->channel_num = 0;
 439
 440        if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
 441                config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
 442        else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 443                config->video_mode =
 444                                MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
 445        else
 446                config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
 447
 448        return 0;
 449}
 450
 451int mdfld_dsi_panel_reset(int pipe)
 452{
 453        unsigned gpio;
 454        int ret = 0;
 455
 456        switch (pipe) {
 457        case 0:
 458                gpio = 128;
 459                break;
 460        case 2:
 461                gpio = 34;
 462                break;
 463        default:
 464                DRM_ERROR("Invalid output\n");
 465                return -EINVAL;
 466        }
 467
 468        ret = gpio_request(gpio, "gfx");
 469        if (ret) {
 470                DRM_ERROR("gpio_rqueset failed\n");
 471                return ret;
 472        }
 473
 474        ret = gpio_direction_output(gpio, 1);
 475        if (ret) {
 476                DRM_ERROR("gpio_direction_output failed\n");
 477                goto gpio_error;
 478        }
 479
 480        gpio_get_value(128);
 481
 482gpio_error:
 483        if (gpio_is_valid(gpio))
 484                gpio_free(gpio);
 485
 486        return ret;
 487}
 488
 489/*
 490 * MIPI output init
 491 * @dev drm device
 492 * @pipe pipe number. 0 or 2
 493 * @config
 494 *
 495 * Do the initialization of a MIPI output, including create DRM mode objects
 496 * initialization of DSI output on @pipe
 497 */
 498void mdfld_dsi_output_init(struct drm_device *dev,
 499                           int pipe,
 500                           const struct panel_funcs *p_vid_funcs)
 501{
 502        struct mdfld_dsi_config *dsi_config;
 503        struct mdfld_dsi_connector *dsi_connector;
 504        struct drm_connector *connector;
 505        struct mdfld_dsi_encoder *encoder;
 506        struct drm_psb_private *dev_priv = dev->dev_private;
 507        struct panel_info dsi_panel_info;
 508        u32 width_mm, height_mm;
 509
 510        dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
 511
 512        if (pipe != 0 && pipe != 2) {
 513                DRM_ERROR("Invalid parameter\n");
 514                return;
 515        }
 516
 517        /*create a new connetor*/
 518        dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
 519        if (!dsi_connector) {
 520                DRM_ERROR("No memory");
 521                return;
 522        }
 523
 524        dsi_connector->pipe =  pipe;
 525
 526        dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
 527                        GFP_KERNEL);
 528        if (!dsi_config) {
 529                DRM_ERROR("cannot allocate memory for DSI config\n");
 530                goto dsi_init_err0;
 531        }
 532        mdfld_dsi_get_default_config(dev, dsi_config, pipe);
 533
 534        dsi_connector->private = dsi_config;
 535
 536        dsi_config->changed = 1;
 537        dsi_config->dev = dev;
 538
 539        dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
 540        if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
 541                        goto dsi_init_err0;
 542
 543        width_mm = dsi_panel_info.width_mm;
 544        height_mm = dsi_panel_info.height_mm;
 545
 546        dsi_config->mode = dsi_config->fixed_mode;
 547        dsi_config->connector = dsi_connector;
 548
 549        if (!dsi_config->fixed_mode) {
 550                DRM_ERROR("No pannel fixed mode was found\n");
 551                goto dsi_init_err0;
 552        }
 553
 554        if (pipe && dev_priv->dsi_configs[0]) {
 555                dsi_config->dvr_ic_inited = 0;
 556                dev_priv->dsi_configs[1] = dsi_config;
 557        } else if (pipe == 0) {
 558                dsi_config->dvr_ic_inited = 1;
 559                dev_priv->dsi_configs[0] = dsi_config;
 560        } else {
 561                DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
 562                goto dsi_init_err0;
 563        }
 564
 565
 566        connector = &dsi_connector->base.base;
 567        drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
 568                                                DRM_MODE_CONNECTOR_LVDS);
 569        drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
 570
 571        connector->display_info.subpixel_order = SubPixelHorizontalRGB;
 572        connector->display_info.width_mm = width_mm;
 573        connector->display_info.height_mm = height_mm;
 574        connector->interlace_allowed = false;
 575        connector->doublescan_allowed = false;
 576
 577        /*attach properties*/
 578        drm_object_attach_property(&connector->base,
 579                                dev->mode_config.scaling_mode_property,
 580                                DRM_MODE_SCALE_FULLSCREEN);
 581        drm_object_attach_property(&connector->base,
 582                                dev_priv->backlight_property,
 583                                MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
 584
 585        /*init DSI package sender on this output*/
 586        if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
 587                DRM_ERROR("Package Sender initialization failed on pipe %d\n",
 588                                                                        pipe);
 589                goto dsi_init_err0;
 590        }
 591
 592        encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
 593        if (!encoder) {
 594                DRM_ERROR("Create DPI encoder failed\n");
 595                goto dsi_init_err1;
 596        }
 597        encoder->private = dsi_config;
 598        dsi_config->encoder = encoder;
 599        encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
 600                INTEL_OUTPUT_MIPI2;
 601        drm_sysfs_connector_add(connector);
 602        return;
 603
 604        /*TODO: add code to destroy outputs on error*/
 605dsi_init_err1:
 606        /*destroy sender*/
 607        mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
 608
 609        drm_connector_cleanup(connector);
 610
 611        kfree(dsi_config->fixed_mode);
 612        kfree(dsi_config);
 613dsi_init_err0:
 614        kfree(dsi_connector);
 615}
 616