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