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 = sender->dev;
  96        struct drm_psb_private *dev_priv = dev->dev_private;
  97        u32 gen_ctrl_val;
  98
  99        if (!sender) {
 100                DRM_ERROR("No sender found\n");
 101                return;
 102        }
 103
 104        /* Set default display backlight value to 85% (0xd8)*/
 105        mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
 106                                true);
 107
 108        /* Set minimum brightness setting of CABC function to 20% (0x33)*/
 109        mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
 110
 111        /* Enable backlight or/and LABC */
 112        gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
 113                                                                BACKLIGHT_ON;
 114        if (LABC_control == 1)
 115                gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
 116                                                                | GAMMA_AUTO;
 117
 118        if (LABC_control == 1)
 119                gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
 120
 121        dev_priv->mipi_ctrl_display = gen_ctrl_val;
 122
 123        mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
 124                                1, true);
 125
 126        mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
 127}
 128
 129void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
 130{
 131        struct mdfld_dsi_pkg_sender *sender;
 132        struct drm_psb_private *dev_priv;
 133        struct mdfld_dsi_config *dsi_config;
 134        u32 gen_ctrl_val = 0;
 135        int p_type = TMD_VID;
 136
 137        if (!dev || (pipe != 0 && pipe != 2)) {
 138                DRM_ERROR("Invalid parameter\n");
 139                return;
 140        }
 141
 142        p_type = mdfld_get_panel_type(dev, 0);
 143
 144        dev_priv = dev->dev_private;
 145
 146        if (pipe)
 147                dsi_config = dev_priv->dsi_configs[1];
 148        else
 149                dsi_config = dev_priv->dsi_configs[0];
 150
 151        sender = mdfld_dsi_get_pkg_sender(dsi_config);
 152
 153        if (!sender) {
 154                DRM_ERROR("No sender found\n");
 155                return;
 156        }
 157
 158        gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
 159
 160        dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
 161                                                        pipe, gen_ctrl_val);
 162
 163        if (p_type == TMD_VID) {
 164                /* Set display backlight value */
 165                mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
 166                                        (u8)gen_ctrl_val, 1, true);
 167        } else {
 168                /* Set display backlight value */
 169                mdfld_dsi_send_mcs_short(sender, write_display_brightness,
 170                                        (u8)gen_ctrl_val, 1, true);
 171
 172                /* Enable backlight control */
 173                if (level == 0)
 174                        gen_ctrl_val = 0;
 175                else
 176                        gen_ctrl_val = dev_priv->mipi_ctrl_display;
 177
 178                mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
 179                                        (u8)gen_ctrl_val, 1, true);
 180        }
 181}
 182
 183static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
 184                                u8 dcs, u32 *data, bool hs)
 185{
 186        struct mdfld_dsi_pkg_sender *sender
 187                = mdfld_dsi_get_pkg_sender(dsi_config);
 188
 189        if (!sender || !data) {
 190                DRM_ERROR("Invalid parameter\n");
 191                return -EINVAL;
 192        }
 193
 194        return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
 195}
 196
 197int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
 198                        bool hs)
 199{
 200        if (!dsi_config || !mode) {
 201                DRM_ERROR("Invalid parameter\n");
 202                return -EINVAL;
 203        }
 204
 205        return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
 206}
 207
 208/*
 209 * NOTE: this function was used by OSPM.
 210 * TODO: will be removed later, should work out display interfaces for OSPM
 211 */
 212void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
 213{
 214        if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
 215                DRM_ERROR("Invalid parameters\n");
 216                return;
 217        }
 218
 219        mdfld_dsi_dpi_controller_init(dsi_config, pipe);
 220}
 221
 222static void mdfld_dsi_connector_save(struct drm_connector *connector)
 223{
 224}
 225
 226static void mdfld_dsi_connector_restore(struct drm_connector *connector)
 227{
 228}
 229
 230/* FIXME: start using the force parameter */
 231static enum drm_connector_status
 232mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
 233{
 234        struct mdfld_dsi_connector *dsi_connector
 235                = mdfld_dsi_connector(connector);
 236
 237        dsi_connector->status = connector_status_connected;
 238
 239        return dsi_connector->status;
 240}
 241
 242static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
 243                                struct drm_property *property,
 244                                uint64_t value)
 245{
 246        struct drm_encoder *encoder = connector->encoder;
 247
 248        if (!strcmp(property->name, "scaling mode") && encoder) {
 249                struct psb_intel_crtc *psb_crtc =
 250                                        to_psb_intel_crtc(encoder->crtc);
 251                bool centerechange;
 252                uint64_t val;
 253
 254                if (!psb_crtc)
 255                        goto set_prop_error;
 256
 257                switch (value) {
 258                case DRM_MODE_SCALE_FULLSCREEN:
 259                        break;
 260                case DRM_MODE_SCALE_NO_SCALE:
 261                        break;
 262                case DRM_MODE_SCALE_ASPECT:
 263                        break;
 264                default:
 265                        goto set_prop_error;
 266                }
 267
 268                if (drm_object_property_get_value(&connector->base, property, &val))
 269                        goto set_prop_error;
 270
 271                if (val == value)
 272                        goto set_prop_done;
 273
 274                if (drm_object_property_set_value(&connector->base,
 275                                                        property, value))
 276                        goto set_prop_error;
 277
 278                centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
 279                        (value == DRM_MODE_SCALE_NO_SCALE);
 280
 281                if (psb_crtc->saved_mode.hdisplay != 0 &&
 282                    psb_crtc->saved_mode.vdisplay != 0) {
 283                        if (centerechange) {
 284                                if (!drm_crtc_helper_set_mode(encoder->crtc,
 285                                                &psb_crtc->saved_mode,
 286                                                encoder->crtc->x,
 287                                                encoder->crtc->y,
 288                                                encoder->crtc->fb))
 289                                        goto set_prop_error;
 290                        } else {
 291                                struct drm_encoder_helper_funcs *funcs =
 292                                                encoder->helper_private;
 293                                funcs->mode_set(encoder,
 294                                        &psb_crtc->saved_mode,
 295                                        &psb_crtc->saved_adjusted_mode);
 296                        }
 297                }
 298        } else if (!strcmp(property->name, "backlight") && encoder) {
 299                if (drm_object_property_set_value(&connector->base, property,
 300                                                                        value))
 301                        goto set_prop_error;
 302                else
 303                        gma_backlight_set(encoder->dev, value);
 304        }
 305set_prop_done:
 306        return 0;
 307set_prop_error:
 308        return -1;
 309}
 310
 311static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
 312{
 313        struct mdfld_dsi_connector *dsi_connector =
 314                                        mdfld_dsi_connector(connector);
 315        struct mdfld_dsi_pkg_sender *sender;
 316
 317        if (!dsi_connector)
 318                return;
 319        drm_sysfs_connector_remove(connector);
 320        drm_connector_cleanup(connector);
 321        sender = dsi_connector->pkg_sender;
 322        mdfld_dsi_pkg_sender_destroy(sender);
 323        kfree(dsi_connector);
 324}
 325
 326static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
 327{
 328        struct mdfld_dsi_connector *dsi_connector =
 329                                mdfld_dsi_connector(connector);
 330        struct mdfld_dsi_config *dsi_config =
 331                                mdfld_dsi_get_config(dsi_connector);
 332        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 333        struct drm_display_mode *dup_mode = NULL;
 334        struct drm_device *dev = connector->dev;
 335
 336        connector->display_info.min_vfreq = 0;
 337        connector->display_info.max_vfreq = 200;
 338        connector->display_info.min_hfreq = 0;
 339        connector->display_info.max_hfreq = 200;
 340
 341        if (fixed_mode) {
 342                dev_dbg(dev->dev, "fixed_mode %dx%d\n",
 343                                fixed_mode->hdisplay, fixed_mode->vdisplay);
 344                dup_mode = drm_mode_duplicate(dev, fixed_mode);
 345                drm_mode_probed_add(connector, dup_mode);
 346                return 1;
 347        }
 348        DRM_ERROR("Didn't get any modes!\n");
 349        return 0;
 350}
 351
 352static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
 353                                                struct drm_display_mode *mode)
 354{
 355        struct mdfld_dsi_connector *dsi_connector =
 356                                        mdfld_dsi_connector(connector);
 357        struct mdfld_dsi_config *dsi_config =
 358                                        mdfld_dsi_get_config(dsi_connector);
 359        struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
 360
 361        if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
 362                return MODE_NO_DBLESCAN;
 363
 364        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 365                return MODE_NO_INTERLACE;
 366
 367        /**
 368         * FIXME: current DC has no fitting unit, reject any mode setting
 369         * request
 370         * Will figure out a way to do up-scaling(pannel fitting) later.
 371         **/
 372        if (fixed_mode) {
 373                if (mode->hdisplay != fixed_mode->hdisplay)
 374                        return MODE_PANEL;
 375
 376                if (mode->vdisplay != fixed_mode->vdisplay)
 377                        return MODE_PANEL;
 378        }
 379
 380        return MODE_OK;
 381}
 382
 383static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
 384{
 385        if (mode == connector->dpms)
 386                return;
 387
 388        /*first, execute dpms*/
 389
 390        drm_helper_connector_dpms(connector, mode);
 391}
 392
 393static struct drm_encoder *mdfld_dsi_connector_best_encoder(
 394                                struct drm_connector *connector)
 395{
 396        struct mdfld_dsi_connector *dsi_connector =
 397                                mdfld_dsi_connector(connector);
 398        struct mdfld_dsi_config *dsi_config =
 399                                mdfld_dsi_get_config(dsi_connector);
 400        return &dsi_config->encoder->base.base;
 401}
 402
 403/*DSI connector funcs*/
 404static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
 405        .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
 406        .save = mdfld_dsi_connector_save,
 407        .restore = mdfld_dsi_connector_restore,
 408        .detect = mdfld_dsi_connector_detect,
 409        .fill_modes = drm_helper_probe_single_connector_modes,
 410        .set_property = mdfld_dsi_connector_set_property,
 411        .destroy = mdfld_dsi_connector_destroy,
 412};
 413
 414/*DSI connector helper funcs*/
 415static const struct drm_connector_helper_funcs
 416        mdfld_dsi_connector_helper_funcs = {
 417        .get_modes = mdfld_dsi_connector_get_modes,
 418        .mode_valid = mdfld_dsi_connector_mode_valid,
 419        .best_encoder = mdfld_dsi_connector_best_encoder,
 420};
 421
 422static int mdfld_dsi_get_default_config(struct drm_device *dev,
 423                                struct mdfld_dsi_config *config, int pipe)
 424{
 425        if (!dev || !config) {
 426                DRM_ERROR("Invalid parameters");
 427                return -EINVAL;
 428        }
 429
 430        config->bpp = 24;
 431        if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 432                config->lane_count = 4;
 433        else
 434                config->lane_count = 2;
 435        config->channel_num = 0;
 436
 437        if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
 438                config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
 439        else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
 440                config->video_mode =
 441                                MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
 442        else
 443                config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
 444
 445        return 0;
 446}
 447
 448int mdfld_dsi_panel_reset(int pipe)
 449{
 450        unsigned gpio;
 451        int ret = 0;
 452
 453        switch (pipe) {
 454        case 0:
 455                gpio = 128;
 456                break;
 457        case 2:
 458                gpio = 34;
 459                break;
 460        default:
 461                DRM_ERROR("Invalid output\n");
 462                return -EINVAL;
 463        }
 464
 465        ret = gpio_request(gpio, "gfx");
 466        if (ret) {
 467                DRM_ERROR("gpio_rqueset failed\n");
 468                return ret;
 469        }
 470
 471        ret = gpio_direction_output(gpio, 1);
 472        if (ret) {
 473                DRM_ERROR("gpio_direction_output failed\n");
 474                goto gpio_error;
 475        }
 476
 477        gpio_get_value(128);
 478
 479gpio_error:
 480        if (gpio_is_valid(gpio))
 481                gpio_free(gpio);
 482
 483        return ret;
 484}
 485
 486/*
 487 * MIPI output init
 488 * @dev drm device
 489 * @pipe pipe number. 0 or 2
 490 * @config
 491 *
 492 * Do the initialization of a MIPI output, including create DRM mode objects
 493 * initialization of DSI output on @pipe
 494 */
 495void mdfld_dsi_output_init(struct drm_device *dev,
 496                           int pipe,
 497                           const struct panel_funcs *p_vid_funcs)
 498{
 499        struct mdfld_dsi_config *dsi_config;
 500        struct mdfld_dsi_connector *dsi_connector;
 501        struct drm_connector *connector;
 502        struct mdfld_dsi_encoder *encoder;
 503        struct drm_psb_private *dev_priv = dev->dev_private;
 504        struct panel_info dsi_panel_info;
 505        u32 width_mm, height_mm;
 506
 507        dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
 508
 509        if (pipe != 0 && pipe != 2) {
 510                DRM_ERROR("Invalid parameter\n");
 511                return;
 512        }
 513
 514        /*create a new connetor*/
 515        dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
 516        if (!dsi_connector) {
 517                DRM_ERROR("No memory");
 518                return;
 519        }
 520
 521        dsi_connector->pipe =  pipe;
 522
 523        dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
 524                        GFP_KERNEL);
 525        if (!dsi_config) {
 526                DRM_ERROR("cannot allocate memory for DSI config\n");
 527                goto dsi_init_err0;
 528        }
 529        mdfld_dsi_get_default_config(dev, dsi_config, pipe);
 530
 531        dsi_connector->private = dsi_config;
 532
 533        dsi_config->changed = 1;
 534        dsi_config->dev = dev;
 535
 536        dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
 537        if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
 538                        goto dsi_init_err0;
 539
 540        width_mm = dsi_panel_info.width_mm;
 541        height_mm = dsi_panel_info.height_mm;
 542
 543        dsi_config->mode = dsi_config->fixed_mode;
 544        dsi_config->connector = dsi_connector;
 545
 546        if (!dsi_config->fixed_mode) {
 547                DRM_ERROR("No pannel fixed mode was found\n");
 548                goto dsi_init_err0;
 549        }
 550
 551        if (pipe && dev_priv->dsi_configs[0]) {
 552                dsi_config->dvr_ic_inited = 0;
 553                dev_priv->dsi_configs[1] = dsi_config;
 554        } else if (pipe == 0) {
 555                dsi_config->dvr_ic_inited = 1;
 556                dev_priv->dsi_configs[0] = dsi_config;
 557        } else {
 558                DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
 559                goto dsi_init_err0;
 560        }
 561
 562
 563        connector = &dsi_connector->base.base;
 564        drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
 565                                                DRM_MODE_CONNECTOR_LVDS);
 566        drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
 567
 568        connector->display_info.subpixel_order = SubPixelHorizontalRGB;
 569        connector->display_info.width_mm = width_mm;
 570        connector->display_info.height_mm = height_mm;
 571        connector->interlace_allowed = false;
 572        connector->doublescan_allowed = false;
 573
 574        /*attach properties*/
 575        drm_object_attach_property(&connector->base,
 576                                dev->mode_config.scaling_mode_property,
 577                                DRM_MODE_SCALE_FULLSCREEN);
 578        drm_object_attach_property(&connector->base,
 579                                dev_priv->backlight_property,
 580                                MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
 581
 582        /*init DSI package sender on this output*/
 583        if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
 584                DRM_ERROR("Package Sender initialization failed on pipe %d\n",
 585                                                                        pipe);
 586                goto dsi_init_err0;
 587        }
 588
 589        encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
 590        if (!encoder) {
 591                DRM_ERROR("Create DPI encoder failed\n");
 592                goto dsi_init_err1;
 593        }
 594        encoder->private = dsi_config;
 595        dsi_config->encoder = encoder;
 596        encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
 597                INTEL_OUTPUT_MIPI2;
 598        drm_sysfs_connector_add(connector);
 599        return;
 600
 601        /*TODO: add code to destroy outputs on error*/
 602dsi_init_err1:
 603        /*destroy sender*/
 604        mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
 605
 606        drm_connector_cleanup(connector);
 607
 608        kfree(dsi_config->fixed_mode);
 609        kfree(dsi_config);
 610dsi_init_err0:
 611        kfree(dsi_connector);
 612}
 613