18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2010 Intel Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
218c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Authors:
248c2ecf20Sopenharmony_ci * jim liu <jim.liu@intel.com>
258c2ecf20Sopenharmony_ci * Jackie Li<yaodong.li@intel.com>
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/delay.h>
298c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
308c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
318c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <asm/intel_scu_ipc.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "mdfld_dsi_dpi.h"
368c2ecf20Sopenharmony_ci#include "mdfld_dsi_output.h"
378c2ecf20Sopenharmony_ci#include "mdfld_dsi_pkg_sender.h"
388c2ecf20Sopenharmony_ci#include "mdfld_output.h"
398c2ecf20Sopenharmony_ci#include "tc35876x-dsi-lvds.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* get the LABC from command line. */
428c2ecf20Sopenharmony_cistatic int LABC_control = 1;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#ifdef MODULE
458c2ecf20Sopenharmony_cimodule_param(LABC_control, int, 0644);
468c2ecf20Sopenharmony_ci#else
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int __init parse_LABC_control(char *arg)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	/* LABC control can be passed in as a cmdline parameter */
518c2ecf20Sopenharmony_ci	/* to enable this feature add LABC=1 to cmdline */
528c2ecf20Sopenharmony_ci	/* to disable this feature add LABC=0 to cmdline */
538c2ecf20Sopenharmony_ci	if (!arg)
548c2ecf20Sopenharmony_ci		return -EINVAL;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (!strcasecmp(arg, "0"))
578c2ecf20Sopenharmony_ci		LABC_control = 0;
588c2ecf20Sopenharmony_ci	else if (!strcasecmp(arg, "1"))
598c2ecf20Sopenharmony_ci		LABC_control = 1;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ciearly_param("LABC", parse_LABC_control);
648c2ecf20Sopenharmony_ci#endif
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * Check and see if the generic control or data buffer is empty and ready.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_civoid mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg,
708c2ecf20Sopenharmony_ci							u32 fifo_stat)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	u32 GEN_BF_time_out_count;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Check MIPI Adatper command registers */
758c2ecf20Sopenharmony_ci	for (GEN_BF_time_out_count = 0;
768c2ecf20Sopenharmony_ci			GEN_BF_time_out_count < GEN_FB_TIME_OUT;
778c2ecf20Sopenharmony_ci			GEN_BF_time_out_count++) {
788c2ecf20Sopenharmony_ci		if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat)
798c2ecf20Sopenharmony_ci			break;
808c2ecf20Sopenharmony_ci		udelay(100);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (GEN_BF_time_out_count == GEN_FB_TIME_OUT)
848c2ecf20Sopenharmony_ci		DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n",
858c2ecf20Sopenharmony_ci					gen_fifo_stat_reg);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/**
898c2ecf20Sopenharmony_ci * Manage the DSI MIPI keyboard and display brightness.
908c2ecf20Sopenharmony_ci * FIXME: this is exported to OSPM code. should work out an specific
918c2ecf20Sopenharmony_ci * display interface to OSPM.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_civoid mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct mdfld_dsi_pkg_sender *sender =
978c2ecf20Sopenharmony_ci				mdfld_dsi_get_pkg_sender(dsi_config);
988c2ecf20Sopenharmony_ci	struct drm_device *dev;
998c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv;
1008c2ecf20Sopenharmony_ci	u32 gen_ctrl_val;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!sender) {
1038c2ecf20Sopenharmony_ci		DRM_ERROR("No sender found\n");
1048c2ecf20Sopenharmony_ci		return;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	dev = sender->dev;
1088c2ecf20Sopenharmony_ci	dev_priv = dev->dev_private;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Set default display backlight value to 85% (0xd8)*/
1118c2ecf20Sopenharmony_ci	mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
1128c2ecf20Sopenharmony_ci				true);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Set minimum brightness setting of CABC function to 20% (0x33)*/
1158c2ecf20Sopenharmony_ci	mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Enable backlight or/and LABC */
1188c2ecf20Sopenharmony_ci	gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
1198c2ecf20Sopenharmony_ci								BACKLIGHT_ON;
1208c2ecf20Sopenharmony_ci	if (LABC_control == 1)
1218c2ecf20Sopenharmony_ci		gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
1228c2ecf20Sopenharmony_ci								| GAMMA_AUTO;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (LABC_control == 1)
1258c2ecf20Sopenharmony_ci		gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	dev_priv->mipi_ctrl_display = gen_ctrl_val;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
1308c2ecf20Sopenharmony_ci				1, true);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_civoid mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct mdfld_dsi_pkg_sender *sender;
1388c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv;
1398c2ecf20Sopenharmony_ci	struct mdfld_dsi_config *dsi_config;
1408c2ecf20Sopenharmony_ci	u32 gen_ctrl_val = 0;
1418c2ecf20Sopenharmony_ci	int p_type = TMD_VID;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (!dev || (pipe != 0 && pipe != 2)) {
1448c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameter\n");
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	p_type = mdfld_get_panel_type(dev, 0);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	dev_priv = dev->dev_private;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (pipe)
1538c2ecf20Sopenharmony_ci		dsi_config = dev_priv->dsi_configs[1];
1548c2ecf20Sopenharmony_ci	else
1558c2ecf20Sopenharmony_ci		dsi_config = dev_priv->dsi_configs[0];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	sender = mdfld_dsi_get_pkg_sender(dsi_config);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!sender) {
1608c2ecf20Sopenharmony_ci		DRM_ERROR("No sender found\n");
1618c2ecf20Sopenharmony_ci		return;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
1678c2ecf20Sopenharmony_ci							pipe, gen_ctrl_val);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (p_type == TMD_VID) {
1708c2ecf20Sopenharmony_ci		/* Set display backlight value */
1718c2ecf20Sopenharmony_ci		mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
1728c2ecf20Sopenharmony_ci					(u8)gen_ctrl_val, 1, true);
1738c2ecf20Sopenharmony_ci	} else {
1748c2ecf20Sopenharmony_ci		/* Set display backlight value */
1758c2ecf20Sopenharmony_ci		mdfld_dsi_send_mcs_short(sender, write_display_brightness,
1768c2ecf20Sopenharmony_ci					(u8)gen_ctrl_val, 1, true);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		/* Enable backlight control */
1798c2ecf20Sopenharmony_ci		if (level == 0)
1808c2ecf20Sopenharmony_ci			gen_ctrl_val = 0;
1818c2ecf20Sopenharmony_ci		else
1828c2ecf20Sopenharmony_ci			gen_ctrl_val = dev_priv->mipi_ctrl_display;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
1858c2ecf20Sopenharmony_ci					(u8)gen_ctrl_val, 1, true);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
1908c2ecf20Sopenharmony_ci				u8 dcs, u32 *data, bool hs)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct mdfld_dsi_pkg_sender *sender
1938c2ecf20Sopenharmony_ci		= mdfld_dsi_get_pkg_sender(dsi_config);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (!sender || !data) {
1968c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameter\n");
1978c2ecf20Sopenharmony_ci		return -EINVAL;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
2048c2ecf20Sopenharmony_ci			bool hs)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	if (!dsi_config || !mode) {
2078c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameter\n");
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/*
2158c2ecf20Sopenharmony_ci * NOTE: this function was used by OSPM.
2168c2ecf20Sopenharmony_ci * TODO: will be removed later, should work out display interfaces for OSPM
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_civoid mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
2218c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameters\n");
2228c2ecf20Sopenharmony_ci		return;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	mdfld_dsi_dpi_controller_init(dsi_config, pipe);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void mdfld_dsi_connector_save(struct drm_connector *connector)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void mdfld_dsi_connector_restore(struct drm_connector *connector)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/* FIXME: start using the force parameter */
2378c2ecf20Sopenharmony_cistatic enum drm_connector_status
2388c2ecf20Sopenharmony_cimdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector
2418c2ecf20Sopenharmony_ci		= mdfld_dsi_connector(connector);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	dsi_connector->status = connector_status_connected;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return dsi_connector->status;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int mdfld_dsi_connector_set_property(struct drm_connector *connector,
2498c2ecf20Sopenharmony_ci				struct drm_property *property,
2508c2ecf20Sopenharmony_ci				uint64_t value)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = connector->encoder;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!strcmp(property->name, "scaling mode") && encoder) {
2558c2ecf20Sopenharmony_ci		struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc);
2568c2ecf20Sopenharmony_ci		bool centerechange;
2578c2ecf20Sopenharmony_ci		uint64_t val;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		if (!gma_crtc)
2608c2ecf20Sopenharmony_ci			goto set_prop_error;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		switch (value) {
2638c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_FULLSCREEN:
2648c2ecf20Sopenharmony_ci			break;
2658c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_NO_SCALE:
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_ASPECT:
2688c2ecf20Sopenharmony_ci			break;
2698c2ecf20Sopenharmony_ci		default:
2708c2ecf20Sopenharmony_ci			goto set_prop_error;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		if (drm_object_property_get_value(&connector->base, property, &val))
2748c2ecf20Sopenharmony_ci			goto set_prop_error;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		if (val == value)
2778c2ecf20Sopenharmony_ci			goto set_prop_done;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		if (drm_object_property_set_value(&connector->base,
2808c2ecf20Sopenharmony_ci							property, value))
2818c2ecf20Sopenharmony_ci			goto set_prop_error;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
2848c2ecf20Sopenharmony_ci			(value == DRM_MODE_SCALE_NO_SCALE);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		if (gma_crtc->saved_mode.hdisplay != 0 &&
2878c2ecf20Sopenharmony_ci		    gma_crtc->saved_mode.vdisplay != 0) {
2888c2ecf20Sopenharmony_ci			if (centerechange) {
2898c2ecf20Sopenharmony_ci				if (!drm_crtc_helper_set_mode(encoder->crtc,
2908c2ecf20Sopenharmony_ci						&gma_crtc->saved_mode,
2918c2ecf20Sopenharmony_ci						encoder->crtc->x,
2928c2ecf20Sopenharmony_ci						encoder->crtc->y,
2938c2ecf20Sopenharmony_ci						encoder->crtc->primary->fb))
2948c2ecf20Sopenharmony_ci					goto set_prop_error;
2958c2ecf20Sopenharmony_ci			} else {
2968c2ecf20Sopenharmony_ci				const struct drm_encoder_helper_funcs *funcs =
2978c2ecf20Sopenharmony_ci						encoder->helper_private;
2988c2ecf20Sopenharmony_ci				funcs->mode_set(encoder,
2998c2ecf20Sopenharmony_ci					&gma_crtc->saved_mode,
3008c2ecf20Sopenharmony_ci					&gma_crtc->saved_adjusted_mode);
3018c2ecf20Sopenharmony_ci			}
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci	} else if (!strcmp(property->name, "backlight") && encoder) {
3048c2ecf20Sopenharmony_ci		if (drm_object_property_set_value(&connector->base, property,
3058c2ecf20Sopenharmony_ci									value))
3068c2ecf20Sopenharmony_ci			goto set_prop_error;
3078c2ecf20Sopenharmony_ci		else
3088c2ecf20Sopenharmony_ci			gma_backlight_set(encoder->dev, value);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ciset_prop_done:
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ciset_prop_error:
3138c2ecf20Sopenharmony_ci	return -1;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void mdfld_dsi_connector_destroy(struct drm_connector *connector)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector =
3198c2ecf20Sopenharmony_ci					mdfld_dsi_connector(connector);
3208c2ecf20Sopenharmony_ci	struct mdfld_dsi_pkg_sender *sender;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (!dsi_connector)
3238c2ecf20Sopenharmony_ci		return;
3248c2ecf20Sopenharmony_ci	drm_connector_unregister(connector);
3258c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
3268c2ecf20Sopenharmony_ci	sender = dsi_connector->pkg_sender;
3278c2ecf20Sopenharmony_ci	mdfld_dsi_pkg_sender_destroy(sender);
3288c2ecf20Sopenharmony_ci	kfree(dsi_connector);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector =
3348c2ecf20Sopenharmony_ci				mdfld_dsi_connector(connector);
3358c2ecf20Sopenharmony_ci	struct mdfld_dsi_config *dsi_config =
3368c2ecf20Sopenharmony_ci				mdfld_dsi_get_config(dsi_connector);
3378c2ecf20Sopenharmony_ci	struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
3388c2ecf20Sopenharmony_ci	struct drm_display_mode *dup_mode = NULL;
3398c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (fixed_mode) {
3428c2ecf20Sopenharmony_ci		dev_dbg(dev->dev, "fixed_mode %dx%d\n",
3438c2ecf20Sopenharmony_ci				fixed_mode->hdisplay, fixed_mode->vdisplay);
3448c2ecf20Sopenharmony_ci		dup_mode = drm_mode_duplicate(dev, fixed_mode);
3458c2ecf20Sopenharmony_ci		drm_mode_probed_add(connector, dup_mode);
3468c2ecf20Sopenharmony_ci		return 1;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	DRM_ERROR("Didn't get any modes!\n");
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic enum drm_mode_status mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
3538c2ecf20Sopenharmony_ci						struct drm_display_mode *mode)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector =
3568c2ecf20Sopenharmony_ci					mdfld_dsi_connector(connector);
3578c2ecf20Sopenharmony_ci	struct mdfld_dsi_config *dsi_config =
3588c2ecf20Sopenharmony_ci					mdfld_dsi_get_config(dsi_connector);
3598c2ecf20Sopenharmony_ci	struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
3628c2ecf20Sopenharmony_ci		return MODE_NO_DBLESCAN;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
3658c2ecf20Sopenharmony_ci		return MODE_NO_INTERLACE;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/**
3688c2ecf20Sopenharmony_ci	 * FIXME: current DC has no fitting unit, reject any mode setting
3698c2ecf20Sopenharmony_ci	 * request
3708c2ecf20Sopenharmony_ci	 * Will figure out a way to do up-scaling(panel fitting) later.
3718c2ecf20Sopenharmony_ci	 **/
3728c2ecf20Sopenharmony_ci	if (fixed_mode) {
3738c2ecf20Sopenharmony_ci		if (mode->hdisplay != fixed_mode->hdisplay)
3748c2ecf20Sopenharmony_ci			return MODE_PANEL;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (mode->vdisplay != fixed_mode->vdisplay)
3778c2ecf20Sopenharmony_ci			return MODE_PANEL;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return MODE_OK;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic struct drm_encoder *mdfld_dsi_connector_best_encoder(
3848c2ecf20Sopenharmony_ci				struct drm_connector *connector)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector =
3878c2ecf20Sopenharmony_ci				mdfld_dsi_connector(connector);
3888c2ecf20Sopenharmony_ci	struct mdfld_dsi_config *dsi_config =
3898c2ecf20Sopenharmony_ci				mdfld_dsi_get_config(dsi_connector);
3908c2ecf20Sopenharmony_ci	return &dsi_config->encoder->base.base;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/*DSI connector funcs*/
3948c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
3958c2ecf20Sopenharmony_ci	.dpms = drm_helper_connector_dpms,
3968c2ecf20Sopenharmony_ci	.detect = mdfld_dsi_connector_detect,
3978c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
3988c2ecf20Sopenharmony_ci	.set_property = mdfld_dsi_connector_set_property,
3998c2ecf20Sopenharmony_ci	.destroy = mdfld_dsi_connector_destroy,
4008c2ecf20Sopenharmony_ci};
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/*DSI connector helper funcs*/
4038c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs
4048c2ecf20Sopenharmony_ci	mdfld_dsi_connector_helper_funcs = {
4058c2ecf20Sopenharmony_ci	.get_modes = mdfld_dsi_connector_get_modes,
4068c2ecf20Sopenharmony_ci	.mode_valid = mdfld_dsi_connector_mode_valid,
4078c2ecf20Sopenharmony_ci	.best_encoder = mdfld_dsi_connector_best_encoder,
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int mdfld_dsi_get_default_config(struct drm_device *dev,
4118c2ecf20Sopenharmony_ci				struct mdfld_dsi_config *config, int pipe)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	if (!dev || !config) {
4148c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameters");
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	config->bpp = 24;
4198c2ecf20Sopenharmony_ci	if (mdfld_get_panel_type(dev, pipe) == TC35876X)
4208c2ecf20Sopenharmony_ci		config->lane_count = 4;
4218c2ecf20Sopenharmony_ci	else
4228c2ecf20Sopenharmony_ci		config->lane_count = 2;
4238c2ecf20Sopenharmony_ci	config->channel_num = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
4268c2ecf20Sopenharmony_ci		config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
4278c2ecf20Sopenharmony_ci	else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
4288c2ecf20Sopenharmony_ci		config->video_mode =
4298c2ecf20Sopenharmony_ci				MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
4308c2ecf20Sopenharmony_ci	else
4318c2ecf20Sopenharmony_ci		config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ciint mdfld_dsi_panel_reset(struct drm_device *ddev, int pipe)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct device *dev = ddev->dev;
4398c2ecf20Sopenharmony_ci	struct gpio_desc *gpiod;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/*
4428c2ecf20Sopenharmony_ci	 * Raise the GPIO reset line for the corresponding pipe to HIGH,
4438c2ecf20Sopenharmony_ci	 * this is probably because it is active low so this takes the
4448c2ecf20Sopenharmony_ci	 * respective pipe out of reset. (We have no code to put it back
4458c2ecf20Sopenharmony_ci	 * into reset in this driver.)
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	switch (pipe) {
4488c2ecf20Sopenharmony_ci	case 0:
4498c2ecf20Sopenharmony_ci		gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_OUT_HIGH);
4508c2ecf20Sopenharmony_ci		if (IS_ERR(gpiod))
4518c2ecf20Sopenharmony_ci			return PTR_ERR(gpiod);
4528c2ecf20Sopenharmony_ci		break;
4538c2ecf20Sopenharmony_ci	case 2:
4548c2ecf20Sopenharmony_ci		gpiod = gpiod_get(dev, "dsi-pipe2-reset", GPIOD_OUT_HIGH);
4558c2ecf20Sopenharmony_ci		if (IS_ERR(gpiod))
4568c2ecf20Sopenharmony_ci			return PTR_ERR(gpiod);
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	default:
4598c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(dev, "Invalid output pipe\n");
4608c2ecf20Sopenharmony_ci		return -EINVAL;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci	gpiod_put(gpiod);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Flush posted writes on the device */
4658c2ecf20Sopenharmony_ci	gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_ASIS);
4668c2ecf20Sopenharmony_ci	if (IS_ERR(gpiod))
4678c2ecf20Sopenharmony_ci		return PTR_ERR(gpiod);
4688c2ecf20Sopenharmony_ci	gpiod_get_value(gpiod);
4698c2ecf20Sopenharmony_ci	gpiod_put(gpiod);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return 0;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * MIPI output init
4768c2ecf20Sopenharmony_ci * @dev drm device
4778c2ecf20Sopenharmony_ci * @pipe pipe number. 0 or 2
4788c2ecf20Sopenharmony_ci * @config
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci * Do the initialization of a MIPI output, including create DRM mode objects
4818c2ecf20Sopenharmony_ci * initialization of DSI output on @pipe
4828c2ecf20Sopenharmony_ci */
4838c2ecf20Sopenharmony_civoid mdfld_dsi_output_init(struct drm_device *dev,
4848c2ecf20Sopenharmony_ci			   int pipe,
4858c2ecf20Sopenharmony_ci			   const struct panel_funcs *p_vid_funcs)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct mdfld_dsi_config *dsi_config;
4888c2ecf20Sopenharmony_ci	struct mdfld_dsi_connector *dsi_connector;
4898c2ecf20Sopenharmony_ci	struct drm_connector *connector;
4908c2ecf20Sopenharmony_ci	struct mdfld_dsi_encoder *encoder;
4918c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
4928c2ecf20Sopenharmony_ci	struct panel_info dsi_panel_info;
4938c2ecf20Sopenharmony_ci	u32 width_mm, height_mm;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (pipe != 0 && pipe != 2) {
4988c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid parameter\n");
4998c2ecf20Sopenharmony_ci		return;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/*create a new connector*/
5038c2ecf20Sopenharmony_ci	dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
5048c2ecf20Sopenharmony_ci	if (!dsi_connector) {
5058c2ecf20Sopenharmony_ci		DRM_ERROR("No memory");
5068c2ecf20Sopenharmony_ci		return;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	dsi_connector->pipe =  pipe;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
5128c2ecf20Sopenharmony_ci			GFP_KERNEL);
5138c2ecf20Sopenharmony_ci	if (!dsi_config) {
5148c2ecf20Sopenharmony_ci		DRM_ERROR("cannot allocate memory for DSI config\n");
5158c2ecf20Sopenharmony_ci		goto dsi_init_err0;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	mdfld_dsi_get_default_config(dev, dsi_config, pipe);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dsi_connector->private = dsi_config;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	dsi_config->changed = 1;
5228c2ecf20Sopenharmony_ci	dsi_config->dev = dev;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
5258c2ecf20Sopenharmony_ci	if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
5268c2ecf20Sopenharmony_ci			goto dsi_init_err0;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	width_mm = dsi_panel_info.width_mm;
5298c2ecf20Sopenharmony_ci	height_mm = dsi_panel_info.height_mm;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	dsi_config->mode = dsi_config->fixed_mode;
5328c2ecf20Sopenharmony_ci	dsi_config->connector = dsi_connector;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (!dsi_config->fixed_mode) {
5358c2ecf20Sopenharmony_ci		DRM_ERROR("No panel fixed mode was found\n");
5368c2ecf20Sopenharmony_ci		goto dsi_init_err0;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (pipe && dev_priv->dsi_configs[0]) {
5408c2ecf20Sopenharmony_ci		dsi_config->dvr_ic_inited = 0;
5418c2ecf20Sopenharmony_ci		dev_priv->dsi_configs[1] = dsi_config;
5428c2ecf20Sopenharmony_ci	} else if (pipe == 0) {
5438c2ecf20Sopenharmony_ci		dsi_config->dvr_ic_inited = 1;
5448c2ecf20Sopenharmony_ci		dev_priv->dsi_configs[0] = dsi_config;
5458c2ecf20Sopenharmony_ci	} else {
5468c2ecf20Sopenharmony_ci		DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
5478c2ecf20Sopenharmony_ci		goto dsi_init_err0;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	connector = &dsi_connector->base.base;
5528c2ecf20Sopenharmony_ci	dsi_connector->base.save = mdfld_dsi_connector_save;
5538c2ecf20Sopenharmony_ci	dsi_connector->base.restore = mdfld_dsi_connector_restore;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
5568c2ecf20Sopenharmony_ci						DRM_MODE_CONNECTOR_LVDS);
5578c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
5608c2ecf20Sopenharmony_ci	connector->display_info.width_mm = width_mm;
5618c2ecf20Sopenharmony_ci	connector->display_info.height_mm = height_mm;
5628c2ecf20Sopenharmony_ci	connector->interlace_allowed = false;
5638c2ecf20Sopenharmony_ci	connector->doublescan_allowed = false;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/*attach properties*/
5668c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
5678c2ecf20Sopenharmony_ci				dev->mode_config.scaling_mode_property,
5688c2ecf20Sopenharmony_ci				DRM_MODE_SCALE_FULLSCREEN);
5698c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
5708c2ecf20Sopenharmony_ci				dev_priv->backlight_property,
5718c2ecf20Sopenharmony_ci				MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/*init DSI package sender on this output*/
5748c2ecf20Sopenharmony_ci	if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
5758c2ecf20Sopenharmony_ci		DRM_ERROR("Package Sender initialization failed on pipe %d\n",
5768c2ecf20Sopenharmony_ci									pipe);
5778c2ecf20Sopenharmony_ci		goto dsi_init_err0;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
5818c2ecf20Sopenharmony_ci	if (!encoder) {
5828c2ecf20Sopenharmony_ci		DRM_ERROR("Create DPI encoder failed\n");
5838c2ecf20Sopenharmony_ci		goto dsi_init_err1;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	encoder->private = dsi_config;
5868c2ecf20Sopenharmony_ci	dsi_config->encoder = encoder;
5878c2ecf20Sopenharmony_ci	encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
5888c2ecf20Sopenharmony_ci		INTEL_OUTPUT_MIPI2;
5898c2ecf20Sopenharmony_ci	drm_connector_register(connector);
5908c2ecf20Sopenharmony_ci	return;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/*TODO: add code to destroy outputs on error*/
5938c2ecf20Sopenharmony_cidsi_init_err1:
5948c2ecf20Sopenharmony_ci	/*destroy sender*/
5958c2ecf20Sopenharmony_ci	mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	kfree(dsi_config->fixed_mode);
6008c2ecf20Sopenharmony_ci	kfree(dsi_config);
6018c2ecf20Sopenharmony_cidsi_init_err0:
6028c2ecf20Sopenharmony_ci	kfree(dsi_connector);
6038c2ecf20Sopenharmony_ci}
604