aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorJani Nikula <jani.nikula@intel.com>2015-01-16 07:27:23 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2015-01-29 10:51:39 -0500
commit7e9804fdcffc650515c60f524b8b2076ee59e710 (patch)
tree9be7c042bddfecdbd9b55b13a8fce20e7c643f85 /drivers/gpu
parent593e0622f4e415351f0a5148aaf0ce5abf667c05 (diff)
drm/i915/dsi: add drm mipi dsi host support
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 <jani.nikula@intel.com> Reviewed-By: Shobhit Kumar <shobhit.kumar@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/Kconfig1
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c162
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.h18
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_panel_vbt.c3
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
12 select TMPFS 12 select TMPFS
13 select DRM_KMS_HELPER 13 select DRM_KMS_HELPER
14 select DRM_PANEL 14 select DRM_PANEL
15 select DRM_MIPI_DSI
15 # i915 depends on ACPI_VIDEO when ACPI is enabled 16 # i915 depends on ACPI_VIDEO when ACPI is enabled
16 # but for select to work, need to select ACPI_VIDEO's dependencies, ick 17 # but for select to work, need to select ACPI_VIDEO's dependencies, ick
17 select BACKLIGHT_LCD_SUPPORT if ACPI 18 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 1e151e00a614..803e87a5a22f 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -29,6 +29,7 @@
29#include <drm/drm_edid.h> 29#include <drm/drm_edid.h>
30#include <drm/i915_drm.h> 30#include <drm/i915_drm.h>
31#include <drm/drm_panel.h> 31#include <drm/drm_panel.h>
32#include <drm/drm_mipi_dsi.h>
32#include <linux/slab.h> 33#include <linux/slab.h>
33#include "i915_drv.h" 34#include "i915_drv.h"
34#include "intel_drv.h" 35#include "intel_drv.h"
@@ -59,6 +60,149 @@ static void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port)
59 DRM_ERROR("DPI FIFOs are not empty\n"); 60 DRM_ERROR("DPI FIFOs are not empty\n");
60} 61}
61 62
63static void write_data(struct drm_i915_private *dev_priv, u32 reg,
64 const u8 *data, u32 len)
65{
66 u32 i, j;
67
68 for (i = 0; i < len; i += 4) {
69 u32 val = 0;
70
71 for (j = 0; j < min_t(u32, len - i, 4); j++)
72 val |= *data++ << 8 * j;
73
74 I915_WRITE(reg, val);
75 }
76}
77
78static void read_data(struct drm_i915_private *dev_priv, u32 reg,
79 u8 *data, u32 len)
80{
81 u32 i, j;
82
83 for (i = 0; i < len; i += 4) {
84 u32 val = I915_READ(reg);
85
86 for (j = 0; j < min_t(u32, len - i, 4); j++)
87 *data++ = val >> 8 * j;
88 }
89}
90
91static ssize_t intel_dsi_host_transfer(struct mipi_dsi_host *host,
92 const struct mipi_dsi_msg *msg)
93{
94 struct intel_dsi_host *intel_dsi_host = to_intel_dsi_host(host);
95 struct drm_device *dev = intel_dsi_host->intel_dsi->base.base.dev;
96 struct drm_i915_private *dev_priv = dev->dev_private;
97 enum port port = intel_dsi_host->port;
98 struct mipi_dsi_packet packet;
99 ssize_t ret;
100 const u8 *header, *data;
101 u32 data_reg, data_mask, ctrl_reg, ctrl_mask;
102
103 ret = mipi_dsi_create_packet(&packet, msg);
104 if (ret < 0)
105 return ret;
106
107 header = packet.header;
108 data = packet.payload;
109
110 if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
111 data_reg = MIPI_LP_GEN_DATA(port);
112 data_mask = LP_DATA_FIFO_FULL;
113 ctrl_reg = MIPI_LP_GEN_CTRL(port);
114 ctrl_mask = LP_CTRL_FIFO_FULL;
115 } else {
116 data_reg = MIPI_HS_GEN_DATA(port);
117 data_mask = HS_DATA_FIFO_FULL;
118 ctrl_reg = MIPI_HS_GEN_CTRL(port);
119 ctrl_mask = HS_CTRL_FIFO_FULL;
120 }
121
122 /* note: this is never true for reads */
123 if (packet.payload_length) {
124
125 if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(port)) & data_mask) == 0, 50))
126 DRM_ERROR("Timeout waiting for HS/LP DATA FIFO !full\n");
127
128 write_data(dev_priv, data_reg, packet.payload,
129 packet.payload_length);
130 }
131
132 if (msg->rx_len) {
133 I915_WRITE(MIPI_INTR_STAT(port), GEN_READ_DATA_AVAIL);
134 }
135
136 if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(port)) & ctrl_mask) == 0, 50)) {
137 DRM_ERROR("Timeout waiting for HS/LP CTRL FIFO !full\n");
138 }
139
140 I915_WRITE(ctrl_reg, header[2] << 16 | header[1] << 8 | header[0]);
141
142 /* ->rx_len is set only for reads */
143 if (msg->rx_len) {
144 data_mask = GEN_READ_DATA_AVAIL;
145 if (wait_for((I915_READ(MIPI_INTR_STAT(port)) & data_mask) == data_mask, 50))
146 DRM_ERROR("Timeout waiting for read data.\n");
147
148 read_data(dev_priv, data_reg, msg->rx_buf, msg->rx_len);
149 }
150
151 /* XXX: fix for reads and writes */
152 return 4 + packet.payload_length;
153}
154
155static int intel_dsi_host_attach(struct mipi_dsi_host *host,
156 struct mipi_dsi_device *dsi)
157{
158 return 0;
159}
160
161static int intel_dsi_host_detach(struct mipi_dsi_host *host,
162 struct mipi_dsi_device *dsi)
163{
164 return 0;
165}
166
167static const struct mipi_dsi_host_ops intel_dsi_host_ops = {
168 .attach = intel_dsi_host_attach,
169 .detach = intel_dsi_host_detach,
170 .transfer = intel_dsi_host_transfer,
171};
172
173static struct intel_dsi_host *intel_dsi_host_init(struct intel_dsi *intel_dsi,
174 enum port port)
175{
176 struct intel_dsi_host *host;
177 struct mipi_dsi_device *device;
178
179 host = kzalloc(sizeof(*host), GFP_KERNEL);
180 if (!host)
181 return NULL;
182
183 host->base.ops = &intel_dsi_host_ops;
184 host->intel_dsi = intel_dsi;
185 host->port = port;
186
187 /*
188 * We should call mipi_dsi_host_register(&host->base) here, but we don't
189 * have a host->dev, and we don't have OF stuff either. So just use the
190 * dsi framework as a library and hope for the best. Create the dsi
191 * devices by ourselves here too. Need to be careful though, because we
192 * don't initialize any of the driver model devices here.
193 */
194 device = kzalloc(sizeof(*device), GFP_KERNEL);
195 if (!device) {
196 kfree(host);
197 return NULL;
198 }
199
200 device->host = &host->base;
201 host->device = device;
202
203 return host;
204}
205
62static void band_gap_reset(struct drm_i915_private *dev_priv) 206static void band_gap_reset(struct drm_i915_private *dev_priv)
63{ 207{
64 mutex_lock(&dev_priv->dpio_lock); 208 mutex_lock(&dev_priv->dpio_lock);
@@ -809,6 +953,7 @@ void intel_dsi_init(struct drm_device *dev)
809 struct drm_connector *connector; 953 struct drm_connector *connector;
810 struct drm_display_mode *scan, *fixed_mode = NULL; 954 struct drm_display_mode *scan, *fixed_mode = NULL;
811 struct drm_i915_private *dev_priv = dev->dev_private; 955 struct drm_i915_private *dev_priv = dev->dev_private;
956 enum port port;
812 unsigned int i; 957 unsigned int i;
813 958
814 DRM_DEBUG_KMS("\n"); 959 DRM_DEBUG_KMS("\n");
@@ -857,7 +1002,11 @@ void intel_dsi_init(struct drm_device *dev)
857 intel_connector->unregister = intel_connector_unregister; 1002 intel_connector->unregister = intel_connector_unregister;
858 1003
859 /* Pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI port C */ 1004 /* Pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI port C */
860 if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { 1005 if (dev_priv->vbt.dsi.config->dual_link) {
1006 /* XXX: does dual link work on either pipe? */
1007 intel_encoder->crtc_mask = (1 << PIPE_A);
1008 intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C));
1009 } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) {
861 intel_encoder->crtc_mask = (1 << PIPE_A); 1010 intel_encoder->crtc_mask = (1 << PIPE_A);
862 intel_dsi->ports = (1 << PORT_A); 1011 intel_dsi->ports = (1 << PORT_A);
863 } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { 1012 } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) {
@@ -865,6 +1014,17 @@ void intel_dsi_init(struct drm_device *dev)
865 intel_dsi->ports = (1 << PORT_C); 1014 intel_dsi->ports = (1 << PORT_C);
866 } 1015 }
867 1016
1017 /* Create a DSI host (and a device) for each port. */
1018 for_each_dsi_port(port, intel_dsi->ports) {
1019 struct intel_dsi_host *host;
1020
1021 host = intel_dsi_host_init(intel_dsi, port);
1022 if (!host)
1023 goto err;
1024
1025 intel_dsi->dsi_hosts[port] = host;
1026 }
1027
868 for (i = 0; i < ARRAY_SIZE(intel_dsi_drivers); i++) { 1028 for (i = 0; i < ARRAY_SIZE(intel_dsi_drivers); i++) {
869 intel_dsi->panel = intel_dsi_drivers[i].init(intel_dsi, 1029 intel_dsi->panel = intel_dsi_drivers[i].init(intel_dsi,
870 intel_dsi_drivers[i].panel_id); 1030 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 @@
26 26
27#include <drm/drmP.h> 27#include <drm/drmP.h>
28#include <drm/drm_crtc.h> 28#include <drm/drm_crtc.h>
29#include <drm/drm_mipi_dsi.h>
29#include "intel_drv.h" 30#include "intel_drv.h"
30 31
31/* Dual Link support */ 32/* Dual Link support */
@@ -33,10 +34,13 @@
33#define DSI_DUAL_LINK_FRONT_BACK 1 34#define DSI_DUAL_LINK_FRONT_BACK 1
34#define DSI_DUAL_LINK_PIXEL_ALT 2 35#define DSI_DUAL_LINK_PIXEL_ALT 2
35 36
37struct intel_dsi_host;
38
36struct intel_dsi { 39struct intel_dsi {
37 struct intel_encoder base; 40 struct intel_encoder base;
38 41
39 struct drm_panel *panel; 42 struct drm_panel *panel;
43 struct intel_dsi_host *dsi_hosts[I915_MAX_PORTS];
40 44
41 struct intel_connector *attached_connector; 45 struct intel_connector *attached_connector;
42 46
@@ -94,6 +98,20 @@ struct intel_dsi {
94 u16 panel_pwr_cycle_delay; 98 u16 panel_pwr_cycle_delay;
95}; 99};
96 100
101struct intel_dsi_host {
102 struct mipi_dsi_host base;
103 struct intel_dsi *intel_dsi;
104 enum port port;
105
106 /* our little hack */
107 struct mipi_dsi_device *device;
108};
109
110static inline struct intel_dsi_host *to_intel_dsi_host(struct mipi_dsi_host *h)
111{
112 return container_of(h, struct intel_dsi_host, base);
113}
114
97#define for_each_dsi_port(__port, __ports_mask) \ 115#define for_each_dsi_port(__port, __ports_mask) \
98 for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++) \ 116 for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++) \
99 if ((__ports_mask) & (1 << (__port))) 117 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 ac7a24dcf7f7..576c730664e6 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)
399 intel_dsi->dual_link = mipi_config->dual_link; 399 intel_dsi->dual_link = mipi_config->dual_link;
400 intel_dsi->pixel_overlap = mipi_config->pixel_overlap; 400 intel_dsi->pixel_overlap = mipi_config->pixel_overlap;
401 401
402 if (intel_dsi->dual_link)
403 intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C));
404
405 if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666) 402 if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666)
406 bits_per_pixel = 18; 403 bits_per_pixel = 18;
407 else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565) 404 else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565)