Skip to content

Commit

Permalink
drm/i915/dsi: add drm mipi dsi host support
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-By: Shobhit Kumar <[email protected]>
Signed-off-by: Daniel Vetter <[email protected]>
  • Loading branch information
jnikula authored and danvet committed Jan 29, 2015
1 parent 593e062 commit 7e9804f
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 4 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
162 changes: 161 additions & 1 deletion drivers/gpu/drm/i915/intel_dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <drm/drm_edid.h>
#include <drm/i915_drm.h>
#include <drm/drm_panel.h>
#include <drm/drm_mipi_dsi.h>
#include <linux/slab.h>
#include "i915_drv.h"
#include "intel_drv.h"
Expand Down Expand Up @@ -59,6 +60,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);
Expand Down Expand Up @@ -809,6 +953,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");
Expand Down Expand Up @@ -857,14 +1002,29 @@ 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) {
intel_encoder->crtc_mask = (1 << PIPE_B);
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);
Expand Down
18 changes: 18 additions & 0 deletions drivers/gpu/drm/i915/intel_dsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@

#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include "intel_drv.h"

/* Dual Link support */
#define DSI_DUAL_LINK_NONE 0
#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;

Expand Down Expand Up @@ -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)))
Expand Down
3 changes: 0 additions & 3 deletions drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 7e9804f

Please sign in to comment.