diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/mdfld_dsi_output.c')
-rw-r--r-- | drivers/gpu/drm/gma500/mdfld_dsi_output.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c new file mode 100644 index 000000000000..4c2cb4a8ad98 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -0,0 +1,618 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#include <linux/module.h> + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "tc35876x-dsi-lvds.h" +#include <linux/pm_runtime.h> +#include <asm/intel_scu_ipc.h> + +/* get the LABC from command line. */ +static int LABC_control = 1; + +#ifdef MODULE +module_param(LABC_control, int, 0644); +#else + +static int __init parse_LABC_control(char *arg) +{ + /* LABC control can be passed in as a cmdline parameter */ + /* to enable this feature add LABC=1 to cmdline */ + /* to disable this feature add LABC=0 to cmdline */ + if (!arg) + return -EINVAL; + + if (!strcasecmp(arg, "0")) + LABC_control = 0; + else if (!strcasecmp(arg, "1")) + LABC_control = 1; + + return 0; +} +early_param("LABC", parse_LABC_control); +#endif + +/** + * Check and see if the generic control or data buffer is empty and ready. + */ +void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg, + u32 fifo_stat) +{ + u32 GEN_BF_time_out_count; + + /* Check MIPI Adatper command registers */ + for (GEN_BF_time_out_count = 0; + GEN_BF_time_out_count < GEN_FB_TIME_OUT; + GEN_BF_time_out_count++) { + if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat) + break; + udelay(100); + } + + if (GEN_BF_time_out_count == GEN_FB_TIME_OUT) + DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n", + gen_fifo_stat_reg); +} + +/** + * Manage the DSI MIPI keyboard and display brightness. + * FIXME: this is exported to OSPM code. should work out an specific + * display interface to OSPM. + */ + +void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_get_pkg_sender(dsi_config); + struct drm_device *dev = sender->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 gen_ctrl_val; + + if (!sender) { + DRM_ERROR("No sender found\n"); + return; + } + + /* Set default display backlight value to 85% (0xd8)*/ + mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1, + true); + + /* Set minimum brightness setting of CABC function to 20% (0x33)*/ + mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true); + + /* Enable backlight or/and LABC */ + gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON | + BACKLIGHT_ON; + if (LABC_control == 1) + gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO + | GAMMA_AUTO; + + if (LABC_control == 1) + gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON; + + dev_priv->mipi_ctrl_display = gen_ctrl_val; + + mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val, + 1, true); + + mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true); +} + +void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level) +{ + struct mdfld_dsi_pkg_sender *sender; + struct drm_psb_private *dev_priv; + struct mdfld_dsi_config *dsi_config; + u32 gen_ctrl_val = 0; + int p_type = TMD_VID; + + if (!dev || (pipe != 0 && pipe != 2)) { + DRM_ERROR("Invalid parameter\n"); + return; + } + + p_type = mdfld_get_panel_type(dev, 0); + + dev_priv = dev->dev_private; + + if (pipe) + dsi_config = dev_priv->dsi_configs[1]; + else + dsi_config = dev_priv->dsi_configs[0]; + + sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if (!sender) { + DRM_ERROR("No sender found\n"); + return; + } + + gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff; + + dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n", + pipe, gen_ctrl_val); + + if (p_type == TMD_VID) { + /* Set display backlight value */ + mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness, + (u8)gen_ctrl_val, 1, true); + } else { + /* Set display backlight value */ + mdfld_dsi_send_mcs_short(sender, write_display_brightness, + (u8)gen_ctrl_val, 1, true); + + /* Enable backlight control */ + if (level == 0) + gen_ctrl_val = 0; + else + gen_ctrl_val = dev_priv->mipi_ctrl_display; + + mdfld_dsi_send_mcs_short(sender, write_ctrl_display, + (u8)gen_ctrl_val, 1, true); + } +} + +static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config, + u8 dcs, u32 *data, bool hs) +{ + struct mdfld_dsi_pkg_sender *sender + = mdfld_dsi_get_pkg_sender(dsi_config); + + if (!sender || !data) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs); +} + +int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode, + bool hs) +{ + if (!dsi_config || !mode) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs); +} + +/* + * NOTE: this function was used by OSPM. + * TODO: will be removed later, should work out display interfaces for OSPM + */ +void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + if (!dsi_config || ((pipe != 0) && (pipe != 2))) { + DRM_ERROR("Invalid parameters\n"); + return; + } + + mdfld_dsi_dpi_controller_init(dsi_config, pipe); +} + +static void mdfld_dsi_connector_save(struct drm_connector *connector) +{ +} + +static void mdfld_dsi_connector_restore(struct drm_connector *connector) +{ +} + +/* FIXME: start using the force parameter */ +static enum drm_connector_status +mdfld_dsi_connector_detect(struct drm_connector *connector, bool force) +{ + struct mdfld_dsi_connector *dsi_connector + = mdfld_dsi_connector(connector); + + dsi_connector->status = connector_status_connected; + + return dsi_connector->status; +} + +static int mdfld_dsi_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + struct backlight_device *psb_bd; + + if (!strcmp(property->name, "scaling mode") && encoder) { + struct psb_intel_crtc *psb_crtc = + to_psb_intel_crtc(encoder->crtc); + bool centerechange; + uint64_t val; + + if (!psb_crtc) + goto set_prop_error; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + goto set_prop_error; + } + + if (drm_connector_property_get_value(connector, property, &val)) + goto set_prop_error; + + if (val == value) + goto set_prop_done; + + if (drm_connector_property_set_value(connector, + property, value)) + goto set_prop_error; + + centerechange = (val == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (psb_crtc->saved_mode.hdisplay != 0 && + psb_crtc->saved_mode.vdisplay != 0) { + if (centerechange) { + if (!drm_crtc_helper_set_mode(encoder->crtc, + &psb_crtc->saved_mode, + encoder->crtc->x, + encoder->crtc->y, + encoder->crtc->fb)) + goto set_prop_error; + } else { + struct drm_encoder_helper_funcs *funcs = + encoder->helper_private; + funcs->mode_set(encoder, + &psb_crtc->saved_mode, + &psb_crtc->saved_adjusted_mode); + } + } + } else if (!strcmp(property->name, "backlight") && encoder) { + if (drm_connector_property_set_value(connector, property, + value)) + goto set_prop_error; + else { + psb_bd = mdfld_get_backlight_device(); + if (psb_bd) { + psb_bd->props.brightness = value; + mdfld_set_brightness(psb_bd); + } + } + } +set_prop_done: + return 0; +set_prop_error: + return -1; +} + +static void mdfld_dsi_connector_destroy(struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_pkg_sender *sender; + + if (!dsi_connector) + return; + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + sender = dsi_connector->pkg_sender; + mdfld_dsi_pkg_sender_destroy(sender); + kfree(dsi_connector); +} + +static int mdfld_dsi_connector_get_modes(struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + struct drm_display_mode *dup_mode = NULL; + struct drm_device *dev = connector->dev; + + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if (fixed_mode) { + dev_dbg(dev->dev, "fixed_mode %dx%d\n", + fixed_mode->hdisplay, fixed_mode->vdisplay); + dup_mode = drm_mode_duplicate(dev, fixed_mode); + drm_mode_probed_add(connector, dup_mode); + return 1; + } + DRM_ERROR("Didn't get any modes!\n"); + return 0; +} + +static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /** + * FIXME: current DC has no fitting unit, reject any mode setting + * request + * Will figure out a way to do up-scaling(pannel fitting) later. + **/ + if (fixed_mode) { + if (mode->hdisplay != fixed_mode->hdisplay) + return MODE_PANEL; + + if (mode->vdisplay != fixed_mode->vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode) +{ + if (mode == connector->dpms) + return; + + /*first, execute dpms*/ + + drm_helper_connector_dpms(connector, mode); +} + +static struct drm_encoder *mdfld_dsi_connector_best_encoder( + struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + return &dsi_config->encoder->base.base; +} + +/*DSI connector funcs*/ +static const struct drm_connector_funcs mdfld_dsi_connector_funcs = { + .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms, + .save = mdfld_dsi_connector_save, + .restore = mdfld_dsi_connector_restore, + .detect = mdfld_dsi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = mdfld_dsi_connector_set_property, + .destroy = mdfld_dsi_connector_destroy, +}; + +/*DSI connector helper funcs*/ +static const struct drm_connector_helper_funcs + mdfld_dsi_connector_helper_funcs = { + .get_modes = mdfld_dsi_connector_get_modes, + .mode_valid = mdfld_dsi_connector_mode_valid, + .best_encoder = mdfld_dsi_connector_best_encoder, +}; + +static int mdfld_dsi_get_default_config(struct drm_device *dev, + struct mdfld_dsi_config *config, int pipe) +{ + if (!dev || !config) { + DRM_ERROR("Invalid parameters"); + return -EINVAL; + } + + config->bpp = 24; + if (mdfld_get_panel_type(dev, pipe) == TC35876X) + config->lane_count = 4; + else + config->lane_count = 2; + config->channel_num = 0; + + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) + config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE; + else if (mdfld_get_panel_type(dev, pipe) == TC35876X) + config->video_mode = + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS; + else + config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE; + + return 0; +} + +int mdfld_dsi_panel_reset(int pipe) +{ + unsigned gpio; + int ret = 0; + + switch (pipe) { + case 0: + gpio = 128; + break; + case 2: + gpio = 34; + break; + default: + DRM_ERROR("Invalid output\n"); + return -EINVAL; + } + + ret = gpio_request(gpio, "gfx"); + if (ret) { + DRM_ERROR("gpio_rqueset failed\n"); + return ret; + } + + ret = gpio_direction_output(gpio, 1); + if (ret) { + DRM_ERROR("gpio_direction_output failed\n"); + goto gpio_error; + } + + gpio_get_value(128); + +gpio_error: + if (gpio_is_valid(gpio)) + gpio_free(gpio); + + return ret; +} + +/* + * MIPI output init + * @dev drm device + * @pipe pipe number. 0 or 2 + * @config + * + * Do the initialization of a MIPI output, including create DRM mode objects + * initialization of DSI output on @pipe + */ +void mdfld_dsi_output_init(struct drm_device *dev, + int pipe, + const struct panel_funcs *p_vid_funcs) +{ + struct mdfld_dsi_config *dsi_config; + struct mdfld_dsi_connector *dsi_connector; + struct drm_connector *connector; + struct mdfld_dsi_encoder *encoder; + struct drm_psb_private *dev_priv = dev->dev_private; + struct panel_info dsi_panel_info; + u32 width_mm, height_mm; + + dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe); + + if (!dev || ((pipe != 0) && (pipe != 2))) { + DRM_ERROR("Invalid parameter\n"); + return; + } + + /*create a new connetor*/ + dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL); + if (!dsi_connector) { + DRM_ERROR("No memory"); + return; + } + + dsi_connector->pipe = pipe; + + dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), + GFP_KERNEL); + if (!dsi_config) { + DRM_ERROR("cannot allocate memory for DSI config\n"); + goto dsi_init_err0; + } + mdfld_dsi_get_default_config(dev, dsi_config, pipe); + + dsi_connector->private = dsi_config; + + dsi_config->changed = 1; + dsi_config->dev = dev; + + dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev); + if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) + goto dsi_init_err0; + + width_mm = dsi_panel_info.width_mm; + height_mm = dsi_panel_info.height_mm; + + dsi_config->mode = dsi_config->fixed_mode; + dsi_config->connector = dsi_connector; + + if (!dsi_config->fixed_mode) { + DRM_ERROR("No pannel fixed mode was found\n"); + goto dsi_init_err0; + } + + if (pipe && dev_priv->dsi_configs[0]) { + dsi_config->dvr_ic_inited = 0; + dev_priv->dsi_configs[1] = dsi_config; + } else if (pipe == 0) { + dsi_config->dvr_ic_inited = 1; + dev_priv->dsi_configs[0] = dsi_config; + } else { + DRM_ERROR("Trying to init MIPI1 before MIPI0\n"); + goto dsi_init_err0; + } + + + connector = &dsi_connector->base.base; + drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs); + + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->display_info.width_mm = width_mm; + connector->display_info.height_mm = height_mm; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /*attach properties*/ + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); + + /*init DSI package sender on this output*/ + if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) { + DRM_ERROR("Package Sender initialization failed on pipe %d\n", + pipe); + goto dsi_init_err0; + } + + encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs); + if (!encoder) { + DRM_ERROR("Create DPI encoder failed\n"); + goto dsi_init_err1; + } + encoder->private = dsi_config; + dsi_config->encoder = encoder; + encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI : + INTEL_OUTPUT_MIPI2; + drm_sysfs_connector_add(connector); + return; + + /*TODO: add code to destroy outputs on error*/ +dsi_init_err1: + /*destroy sender*/ + mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender); + + drm_connector_cleanup(connector); + + kfree(dsi_config->fixed_mode); + kfree(dsi_config); +dsi_init_err0: + kfree(dsi_connector); +} |