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 gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc);
 253                bool centerechange;
 254                uint64_t val;
 255
 256                if (!gma_crtc)
 257                        goto set_prop_error;
 258
 259                switch (value) {
 260                case DRM_MODE_SCALE_FULLSCREEN:
 261                        break;
 262                case DRM_MODE_SCALE_NO_SCALE:
 263                        break;
 264                case DRM_MODE_SCALE_ASPECT:
 265                        break;
 266                default:
 267                        goto set_prop_error;
 268                }
 269
 270                if (drm_object_property_get_value(&connector->base, property, &val))
 271                        goto set_prop_error;
 272
 273                if (val == value)
 274                        goto set_prop_done;
 275
 276                if (drm_object_property_set_value(&connector->base,
 277                                                        property, value))
 278                        goto set_prop_error;
 279
 280                centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
 281                        (value == DRM_MODE_SCALE_NO_SCALE);
 282
 283                if (gma_crtc->saved_mode.hdisplay != 0 &&
 284                    gma_crtc->saved_mode.vdisplay != 0) {
 285                        if (centerechange) {
 286                                if (!drm_crtc_helper_set_mode(encoder->crtc,
 287                                                &gma_crtc->saved_mode,
 288                                                encoder->crtc->x,
 289                                                encoder->crtc->y,
 290                                                encoder->crtc->primary->fb))
 291                                        goto set_prop_error;
 292                        } else {
 293                                const struct drm_encoder_helper_funcs *funcs =
 294                                                encoder->helper_private;
 295                                funcs->mode_set(encoder,
 296                                        &gma_crtc->saved_mode,
 297                                        &gma_crtc->saved_adjusted_mode);
 298                        }
 299                }
 300        } else if (!strcmp(property->name, "backlight") && encoder) {
 301                if (drm_object_property_set_value(&connector->base, property,
 302                                                                        value))
 303                        goto set_prop_error;
 304                else
 305                        gma_backlight_set(encoder->dev, value);
 306        }
 307set_prop_done:
 308        return 0;
 309set_prop_error:
 310        return -1;
 311}
 312
 313static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
 314{
 315        struct mdfld_dsi_connector *dsi_connector =
 316                                        mdfld_dsi_connector(connector);
 317        struct mdfld_dsi_pkg_sender *sender;
 318
 319        if (!dsi_connector)
 320                return;
 321        drm_connector_unregister(connector);
 322        drm_connector_cleanup(connector);
 323        sender = dsi_connector->pkg_sender;
 324        mdfld_dsi_pkg_sender_destroy(sender);
 325        kfree(dsi_connector);
 326}
 327
 328static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
 329{
 330        struct mdfld_dsi_connector *dsi_connector =
 331                                mdfld_dsi_connector(connector);
 332        struct mdfld_dsi_config *dsi_config =
 333                                mdfld_dsi_get_config(dsi_connector);
 334        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 335        struct drm_display_mode *dup_mode = NULL;
 336        struct drm_device *dev = connector->dev;
 337
 338        connector->display_info.min_vfreq = 0;
 339        connector->display_info.max_vfreq = 200;
 340        connector->display_info.min_hfreq = 0;
 341        connector->display_info.max_hfreq = 200;
 342
 343        if (fixed_mode) {
 344                dev_dbg(dev->dev, "fixed_mode %dx%d\n",
 345                                fixed_mode->hdisplay, fixed_mode->vdisplay);
 346                dup_mode = drm_mode_duplicate(dev, fixed_mode);
 347                drm_mode_probed_add(connector, dup_mode);
 348                return 1;
 349        }
 350        DRM_ERROR("Didn't get any modes!\n");
 351        return 0;
 352}
 353
 354static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
 355                                                struct drm_display_mode *mode)
 356{
 357        struct mdfld_dsi_connector *dsi_connector =
 358                                        mdfld_dsi_connector(connector);
 359        struct mdfld_dsi_config *dsi_config =
 360                                        mdfld_dsi_get_config(dsi_connector);
 361        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 362
 363        if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
 364                return MODE_NO_DBLESCAN;
 365
 366        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 367                return MODE_NO_INTERLACE;
 368
 369        /**
 370         * FIXME: current DC has no fitting unit, reject any mode setting
 371         * request
 372         * Will figure out a way to do up-scaling(pannel fitting) later.
 373         **/
 374        if (fixed_mode) {
 375                if (mode->hdisplay != fixed_mode->hdisplay)
 376                        return MODE_PANEL;
 377
 378                if (mode->vdisplay != fixed_mode->vdisplay)
 379                        return MODE_PANEL;
 380        }
 381
 382        return MODE_OK;
 383}
 384
 385static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
 386{
 387        if (mode == connector->dpms)
 388                return;
 389
 390        /*first, execute dpms*/
 391
 392        drm_helper_connector_dpms(connector, mode);
 393}
 394
 395static struct drm_encoder *mdfld_dsi_connector_best_encoder(
 396                                struct drm_connector *connector)
 397{
 398        struct mdfld_dsi_connector *dsi_connector =
 399                                mdfld_dsi_connector(connector);
 400        struct mdfld_dsi_config *dsi_config =
 401                                mdfld_dsi_get_config(dsi_connector);
 402        return &dsi_config->encoder->base.base;
 403}
 404
 405/*DSI connector funcs*/
 406static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
 407        .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
 408        .save = mdfld_dsi_connector_save,
 409        .restore = mdfld_dsi_connector_restore,
 410        .detect = mdfld_dsi_connector_detect,
 411        .fill_modes = drm_helper_probe_single_connector_modes,
 412        .set_property = mdfld_dsi_connector_set_property,
 413        .destroy = mdfld_dsi_connector_destroy,
 414};
 415
 416/*DSI connector helper funcs*/
 417static const struct drm_connector_helper_funcs
 418        mdfld_dsi_connector_helper_funcs = {
 419        .get_modes = mdfld_dsi_connector_get_modes,
 420        .mode_valid = mdfld_dsi_connector_mode_valid,
 421        .best_encoder = mdfld_dsi_connector_best_encoder,
 422};
 423
 424static int mdfld_dsi_get_default_config(struct drm_device *dev,
 425                                struct mdfld_dsi_config *config, int pipe)
 426{
 427        if (!dev || !config) {
 428                DRM_ERROR("Invalid parameters");
 429                return -EINVAL;
 430        }
 431
 432        config->bpp = 24;
 433        if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 434                config->lane_count = 4;
 435        else
 436                config->lane_count = 2;
 437        config->channel_num = 0;
 438
 439        if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
 440                config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
 441        else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 442                config->video_mode =
 443                                MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
 444        else
 445                config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
 446
 447        return 0;
 448}
 449
 450int mdfld_dsi_panel_reset(int pipe)
 451{
 452        unsigned gpio;
 453        int ret = 0;
 454
 455        switch (pipe) {
 456        case 0:
 457                gpio = 128;
 458                break;
 459        case 2:
 460                gpio = 34;
 461                break;
 462        default:
 463                DRM_ERROR("Invalid output\n");
 464                return -EINVAL;
 465        }
 466
 467        ret = gpio_request(gpio, "gfx");
 468        if (ret) {
 469                DRM_ERROR("gpio_rqueset failed\n");
 470                return ret;
 471        }
 472
 473        ret = gpio_direction_output(gpio, 1);
 474        if (ret) {
 475                DRM_ERROR("gpio_direction_output failed\n");
 476                goto gpio_error;
 477        }
 478
 479        gpio_get_value(128);
 480
 481gpio_error:
 482        if (gpio_is_valid(gpio))
 483                gpio_free(gpio);
 484
 485        return ret;
 486}
 487
 488/*
 489 * MIPI output init
 490 * @dev drm device
 491 * @pipe pipe number. 0 or 2
 492 * @config
 493 *
 494 * Do the initialization of a MIPI output, including create DRM mode objects
 495 * initialization of DSI output on @pipe
 496 */
 497void mdfld_dsi_output_init(struct drm_device *dev,
 498                           int pipe,
 499                           const struct panel_funcs *p_vid_funcs)
 500{
 501        struct mdfld_dsi_config *dsi_config;
 502        struct mdfld_dsi_connector *dsi_connector;
 503        struct drm_connector *connector;
 504        struct mdfld_dsi_encoder *encoder;
 505        struct drm_psb_private *dev_priv = dev->dev_private;
 506        struct panel_info dsi_panel_info;
 507        u32 width_mm, height_mm;
 508
 509        dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
 510
 511        if (pipe != 0 && pipe != 2) {
 512                DRM_ERROR("Invalid parameter\n");
 513                return;
 514        }
 515
 516        /*create a new connetor*/
 517        dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
 518        if (!dsi_connector) {
 519                DRM_ERROR("No memory");
 520                return;
 521        }
 522
 523        dsi_connector->pipe =  pipe;
 524
 525        dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
 526                        GFP_KERNEL);
 527        if (!dsi_config) {
 528                DRM_ERROR("cannot allocate memory for DSI config\n");
 529                goto dsi_init_err0;
 530        }
 531        mdfld_dsi_get_default_config(dev, dsi_config, pipe);
 532
 533        dsi_connector->private = dsi_config;
 534
 535        dsi_config->changed = 1;
 536        dsi_config->dev = dev;
 537
 538        dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
 539        if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
 540                        goto dsi_init_err0;
 541
 542        width_mm = dsi_panel_info.width_mm;
 543        height_mm = dsi_panel_info.height_mm;
 544
 545        dsi_config->mode = dsi_config->fixed_mode;
 546        dsi_config->connector = dsi_connector;
 547
 548        if (!dsi_config->fixed_mode) {
 549                DRM_ERROR("No pannel fixed mode was found\n");
 550                goto dsi_init_err0;
 551        }
 552
 553        if (pipe && dev_priv->dsi_configs[0]) {
 554                dsi_config->dvr_ic_inited = 0;
 555                dev_priv->dsi_configs[1] = dsi_config;
 556        } else if (pipe == 0) {
 557                dsi_config->dvr_ic_inited = 1;
 558                dev_priv->dsi_configs[0] = dsi_config;
 559        } else {
 560                DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
 561                goto dsi_init_err0;
 562        }
 563
 564
 565        connector = &dsi_connector->base.base;
 566        drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
 567                                                DRM_MODE_CONNECTOR_LVDS);
 568        drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
 569
 570        connector->display_info.subpixel_order = SubPixelHorizontalRGB;
 571        connector->display_info.width_mm = width_mm;
 572        connector->display_info.height_mm = height_mm;
 573        connector->interlace_allowed = false;
 574        connector->doublescan_allowed = false;
 575
 576        /*attach properties*/
 577        drm_object_attach_property(&connector->base,
 578                                dev->mode_config.scaling_mode_property,
 579                                DRM_MODE_SCALE_FULLSCREEN);
 580        drm_object_attach_property(&connector->base,
 581                                dev_priv->backlight_property,
 582                                MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
 583
 584        /*init DSI package sender on this output*/
 585        if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
 586                DRM_ERROR("Package Sender initialization failed on pipe %d\n",
 587                                                                        pipe);
 588                goto dsi_init_err0;
 589        }
 590
 591        encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
 592        if (!encoder) {
 593                DRM_ERROR("Create DPI encoder failed\n");
 594                goto dsi_init_err1;
 595        }
 596        encoder->private = dsi_config;
 597        dsi_config->encoder = encoder;
 598        encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
 599                INTEL_OUTPUT_MIPI2;
 600        drm_connector_register(connector);
 601        return;
 602
 603        /*TODO: add code to destroy outputs on error*/
 604dsi_init_err1:
 605        /*destroy sender*/
 606        mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
 607
 608        drm_connector_cleanup(connector);
 609
 610        kfree(dsi_config->fixed_mode);
 611        kfree(dsi_config);
 612dsi_init_err0:
 613        kfree(dsi_connector);
 614}
 615