From patchwork Fri Jan 16 12:27:23 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jani Nikula X-Patchwork-Id: 5648061 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C59F89F3A0 for ; Fri, 16 Jan 2015 12:27:09 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 998CD2012E for ; Fri, 16 Jan 2015 12:27:08 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 5B6DC201E4 for ; Fri, 16 Jan 2015 12:27:07 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id E2434720B0; Fri, 16 Jan 2015 04:27:06 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTP id 44601720BB for ; Fri, 16 Jan 2015 04:27:05 -0800 (PST) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga103.fm.intel.com with ESMTP; 16 Jan 2015 04:21:42 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.09,410,1418112000"; d="scan'208";a="662920065" Received: from jnikula-mobl.fi.intel.com (HELO localhost) ([10.237.72.77]) by fmsmga002.fm.intel.com with ESMTP; 16 Jan 2015 04:27:03 -0800 From: Jani Nikula To: intel-gfx@lists.freedesktop.org Date: Fri, 16 Jan 2015 14:27:23 +0200 Message-Id: X-Mailer: git-send-email 2.1.4 In-Reply-To: References: In-Reply-To: References: Organization: Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo Cc: jani.nikula@intel.com, Shobhit Kumar , Thierry Reding Subject: [Intel-gfx] [RFC PATCH 08/12] drm/i915/dsi: add drm mipi dsi host support X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add basic support for using the drm mipi dsi framework for DSI. We don't use device tree which is pretty much required by mipi_dsi_host_register and friends, and we don't have the kind of device model the functions expect either. So we cheat and use it as a library to abstract what we need: a nice, clean interface for DSI transfers. This means we will have to be careful with what functions we call, as the driver model devices in mipi_dsi_host and mipi_dsi_device will *not* be initialized. Signed-off-by: Jani Nikula Reviewed-By: Shobhit Kumar --- drivers/gpu/drm/i915/Kconfig | 1 + drivers/gpu/drm/i915/intel_dsi.c | 162 ++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_dsi.h | 18 ++++ drivers/gpu/drm/i915/intel_dsi_panel_vbt.c | 3 - 4 files changed, 180 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index da196cd07263..74acca9bcd9d 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -12,6 +12,7 @@ config DRM_I915 select TMPFS select DRM_KMS_HELPER select DRM_PANEL + select DRM_MIPI_DSI # i915 depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick select BACKLIGHT_LCD_SUPPORT if ACPI diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 19a9955eab0e..5cfa3431785a 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "i915_drv.h" #include "intel_drv.h" @@ -58,6 +59,149 @@ static void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port) DRM_ERROR("DPI FIFOs are not empty\n"); } +static void write_data(struct drm_i915_private *dev_priv, u32 reg, + const u8 *data, u32 len) +{ + u32 i, j; + + for (i = 0; i < len; i += 4) { + u32 val = 0; + + for (j = 0; j < min_t(u32, len - i, 4); j++) + val |= *data++ << 8 * j; + + I915_WRITE(reg, val); + } +} + +static void read_data(struct drm_i915_private *dev_priv, u32 reg, + u8 *data, u32 len) +{ + u32 i, j; + + for (i = 0; i < len; i += 4) { + u32 val = I915_READ(reg); + + for (j = 0; j < min_t(u32, len - i, 4); j++) + *data++ = val >> 8 * j; + } +} + +static ssize_t intel_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct intel_dsi_host *intel_dsi_host = to_intel_dsi_host(host); + struct drm_device *dev = intel_dsi_host->intel_dsi->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dsi_host->port; + struct mipi_dsi_packet packet; + ssize_t ret; + const u8 *header, *data; + u32 data_reg, data_mask, ctrl_reg, ctrl_mask; + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret < 0) + return ret; + + header = packet.header; + data = packet.payload; + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) { + data_reg = MIPI_LP_GEN_DATA(port); + data_mask = LP_DATA_FIFO_FULL; + ctrl_reg = MIPI_LP_GEN_CTRL(port); + ctrl_mask = LP_CTRL_FIFO_FULL; + } else { + data_reg = MIPI_HS_GEN_DATA(port); + data_mask = HS_DATA_FIFO_FULL; + ctrl_reg = MIPI_HS_GEN_CTRL(port); + ctrl_mask = HS_CTRL_FIFO_FULL; + } + + /* note: this is never true for reads */ + if (packet.payload_length) { + + if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(port)) & data_mask) == 0, 50)) + DRM_ERROR("Timeout waiting for HS/LP DATA FIFO !full\n"); + + write_data(dev_priv, data_reg, packet.payload, + packet.payload_length); + } + + if (msg->rx_len) { + I915_WRITE(MIPI_INTR_STAT(port), GEN_READ_DATA_AVAIL); + } + + if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(port)) & ctrl_mask) == 0, 50)) { + DRM_ERROR("Timeout waiting for HS/LP CTRL FIFO !full\n"); + } + + I915_WRITE(ctrl_reg, header[2] << 16 | header[1] << 8 | header[0]); + + /* ->rx_len is set only for reads */ + if (msg->rx_len) { + data_mask = GEN_READ_DATA_AVAIL; + if (wait_for((I915_READ(MIPI_INTR_STAT(port)) & data_mask) == data_mask, 50)) + DRM_ERROR("Timeout waiting for read data.\n"); + + read_data(dev_priv, data_reg, msg->rx_buf, msg->rx_len); + } + + /* XXX: fix for reads and writes */ + return 4 + packet.payload_length; +} + +static int intel_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi) +{ + return 0; +} + +static int intel_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi) +{ + return 0; +} + +static const struct mipi_dsi_host_ops intel_dsi_host_ops = { + .attach = intel_dsi_host_attach, + .detach = intel_dsi_host_detach, + .transfer = intel_dsi_host_transfer, +}; + +static struct intel_dsi_host *intel_dsi_host_init(struct intel_dsi *intel_dsi, + enum port port) +{ + struct intel_dsi_host *host; + struct mipi_dsi_device *device; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return NULL; + + host->base.ops = &intel_dsi_host_ops; + host->intel_dsi = intel_dsi; + host->port = port; + + /* + * We should call mipi_dsi_host_register(&host->base) here, but we don't + * have a host->dev, and we don't have OF stuff either. So just use the + * dsi framework as a library and hope for the best. Create the dsi + * devices by ourselves here too. Need to be careful though, because we + * don't initialize any of the driver model devices here. + */ + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) { + kfree(host); + return NULL; + } + + device->host = &host->base; + host->device = device; + + return host; +} + static void band_gap_reset(struct drm_i915_private *dev_priv) { mutex_lock(&dev_priv->dpio_lock); @@ -806,6 +950,7 @@ void intel_dsi_init(struct drm_device *dev) struct drm_connector *connector; struct drm_display_mode *scan, *fixed_mode = NULL; struct drm_i915_private *dev_priv = dev->dev_private; + enum port port; unsigned int i; DRM_DEBUG_KMS("\n"); @@ -854,7 +999,11 @@ void intel_dsi_init(struct drm_device *dev) intel_connector->unregister = intel_connector_unregister; /* Pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI port C */ - if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { + if (dev_priv->vbt.dsi.config->dual_link) { + /* XXX: does dual link work on either pipe? */ + intel_encoder->crtc_mask = (1 << PIPE_A); + intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C)); + } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { intel_encoder->crtc_mask = (1 << PIPE_A); intel_dsi->ports = (1 << PORT_A); } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { @@ -862,6 +1011,17 @@ void intel_dsi_init(struct drm_device *dev) intel_dsi->ports = (1 << PORT_C); } + /* Create a DSI host (and a device) for each port. */ + for_each_dsi_port(port, intel_dsi->ports) { + struct intel_dsi_host *host; + + host = intel_dsi_host_init(intel_dsi, port); + if (!host) + goto err; + + intel_dsi->dsi_hosts[port] = host; + } + for (i = 0; i < ARRAY_SIZE(intel_dsi_drivers); i++) { intel_dsi->panel = intel_dsi_drivers[i].init(intel_dsi, intel_dsi_drivers[i].panel_id); diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h index fc0b2b8d90f1..2784ac442368 100644 --- a/drivers/gpu/drm/i915/intel_dsi.h +++ b/drivers/gpu/drm/i915/intel_dsi.h @@ -26,6 +26,7 @@ #include #include +#include #include "intel_drv.h" /* Dual Link support */ @@ -33,10 +34,13 @@ #define DSI_DUAL_LINK_FRONT_BACK 1 #define DSI_DUAL_LINK_PIXEL_ALT 2 +struct intel_dsi_host; + struct intel_dsi { struct intel_encoder base; struct drm_panel *panel; + struct intel_dsi_host *dsi_hosts[I915_MAX_PORTS]; struct intel_connector *attached_connector; @@ -94,6 +98,20 @@ struct intel_dsi { u16 panel_pwr_cycle_delay; }; +struct intel_dsi_host { + struct mipi_dsi_host base; + struct intel_dsi *intel_dsi; + enum port port; + + /* our little hack */ + struct mipi_dsi_device *device; +}; + +static inline struct intel_dsi_host *to_intel_dsi_host(struct mipi_dsi_host *h) +{ + return container_of(h, struct intel_dsi_host, base); +} + #define for_each_dsi_port(__port, __ports_mask) \ for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++) \ if ((__ports_mask) & (1 << (__port))) diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c index 204f54df8fe1..e363c26a2b05 100644 --- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -399,9 +399,6 @@ struct drm_panel *vbt_panel_init(struct intel_dsi *intel_dsi, u16 panel_id) intel_dsi->dual_link = mipi_config->dual_link; intel_dsi->pixel_overlap = mipi_config->pixel_overlap; - if (intel_dsi->dual_link) - intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C)); - if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666) bits_per_pixel = 18; else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565)