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 00000000000..4c2cb4a8ad9 --- /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); +}  |