On Fri, Aug 16, 2013 at 03:35:56PM +0300, Jani Nikula wrote: > This does not include any panel specific sub-encoders yet. > > v2: Fix fixed mode handling (Daniel) > > Signed-off-by: Jani Nikula <jani.nikula@xxxxxxxxx> > Signed-off-by: Shobhit Kumar <shobhit.kumar@xxxxxxxxx> > --- > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/intel_drv.h | 1 + > drivers/gpu/drm/i915/intel_dsi.c | 534 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 536 insertions(+) > create mode 100644 drivers/gpu/drm/i915/intel_dsi.c > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile > index 8bffd29..5864c5b 100644 > --- a/drivers/gpu/drm/i915/Makefile > +++ b/drivers/gpu/drm/i915/Makefile > @@ -21,6 +21,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ > intel_display.o \ > intel_crt.o \ > intel_lvds.o \ > + intel_dsi.o \ > intel_dsi_cmd.o \ > intel_bios.o \ > intel_ddi.o \ > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index a31abc6..cbe3df1 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -525,6 +525,7 @@ extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj, > struct intel_ring_buffer *ring); > extern void intel_mark_idle(struct drm_device *dev); > extern void intel_lvds_init(struct drm_device *dev); > +extern bool intel_dsi_init(struct drm_device *dev); > extern bool intel_is_dual_link_lvds(struct drm_device *dev); > extern void intel_dp_init(struct drm_device *dev, int output_reg, > enum port port); > diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c > new file mode 100644 > index 0000000..d7eddbd > --- /dev/null > +++ b/drivers/gpu/drm/i915/intel_dsi.c > @@ -0,0 +1,534 @@ > +/* > + * Copyright © 2013 Intel Corporation > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + * > + * Author: Jani Nikula <jani.nikula@xxxxxxxxx> > + */ > + > +#include <drm/drmP.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_edid.h> > +#include <drm/i915_drm.h> > +#include <linux/slab.h> > +#include "i915_drv.h" > +#include "intel_drv.h" > +#include "intel_dsi.h" > +#include "intel_dsi_cmd.h" > + > +/* the sub-encoders aka panel drivers */ > +static const struct intel_dsi_device intel_dsi_devices[] = { > +}; > + > +static struct intel_dsi *intel_attached_dsi(struct drm_connector *connector) > +{ > + return container_of(intel_attached_encoder(connector), > + struct intel_dsi, base); > +} > + > +static inline bool is_vid_mode(struct intel_dsi *intel_dsi) > +{ > + return intel_dsi->dev.type == INTEL_DSI_VIDEO_MODE; > +} > + > +static inline bool is_cmd_mode(struct intel_dsi *intel_dsi) > +{ > + return intel_dsi->dev.type == INTEL_DSI_COMMAND_MODE; > +} > + > +static void intel_dsi_hot_plug(struct intel_encoder *encoder) > +{ > + DRM_DEBUG_KMS("\n"); > +} > + > +static bool intel_dsi_compute_config(struct intel_encoder *encoder, > + struct intel_crtc_config *config) > +{ > + struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi, > + base); > + struct intel_connector *intel_connector = intel_dsi->attached_connector; > + struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; > + struct drm_display_mode *adjusted_mode = &config->adjusted_mode; > + struct drm_display_mode *mode = &config->requested_mode; > + > + DRM_DEBUG_KMS("\n"); > + > + if (fixed_mode) > + intel_fixed_panel_mode(fixed_mode, adjusted_mode); > + > + if (intel_dsi->dev.dev_ops->mode_fixup) > + return intel_dsi->dev.dev_ops->mode_fixup(&intel_dsi->dev, > + mode, adjusted_mode); > + > + return true; > +} > + > +static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder) > +{ > + DRM_DEBUG_KMS("\n"); > +} > + > +static void intel_dsi_pre_enable(struct intel_encoder *encoder) > +{ > + DRM_DEBUG_KMS("\n"); > +} > + > +static void intel_dsi_enable(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); > + struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); > + int pipe = intel_crtc->pipe; > + u32 temp; > + > + DRM_DEBUG_KMS("\n"); > + > + temp = I915_READ(MIPI_DEVICE_READY(pipe)); > + temp &= ~ULPS_STATE_MASK; > + temp &= ~DEVICE_READY; /* XXX: assuming it's already !ready */ The spec seems to be telling me that we shouldn't frob w/ DEVICE_READY when in ULPS. So maybe we should not clear DEVICE_READY? Although it should not be set when we get here, so I guess I'm worrying about nothing. > + I915_WRITE(MIPI_DEVICE_READY(pipe), temp | ULPS_STATE_EXIT); > + msleep(20); /* XXX */ > + I915_WRITE(MIPI_DEVICE_READY(pipe), temp); > + msleep(20); /* XXX */ > + I915_WRITE(MIPI_DEVICE_READY(pipe), temp | DEVICE_READY); > + > + if (is_cmd_mode(intel_dsi)) > + I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(pipe), 8 * 4); > + > + /* Do this here, or let the slave encoder do it in ->dpms? */ > + if (is_vid_mode(intel_dsi)) { > + dpi_send_cmd(intel_dsi, TURN_ON); > + msleep(100); > + } > + > + /* assert ip_tg_enable signal */ > + temp = I915_READ(MIPI_PORT_CTRL(pipe)); > + I915_WRITE(MIPI_PORT_CTRL(pipe), temp | DPI_ENABLE); > + POSTING_READ(MIPI_PORT_CTRL(pipe)); Should be wrapped in is_vid_mode() check, no? > + > + intel_dsi->dev.dev_ops->enable(&intel_dsi->dev); > +} > + > +static void intel_dsi_disable(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); > + struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); > + int pipe = intel_crtc->pipe; > + u32 temp; > + > + DRM_DEBUG_KMS("\n"); > + > + intel_dsi->dev.dev_ops->disable(&intel_dsi->dev); > + > + if (is_vid_mode(intel_dsi)) > + dpi_send_cmd(intel_dsi, SHUTDOWN); > + > + if (is_vid_mode(intel_dsi)) { > + /* de-assert ip_tg_enable signal */ > + temp = I915_READ(MIPI_PORT_CTRL(pipe)); > + I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE); > + POSTING_READ(MIPI_PORT_CTRL(pipe)); > + } > + > + /* XXX: wait for fifos before ulps entry */ > + > + temp = I915_READ(MIPI_DEVICE_READY(pipe)); > + temp &= ~ULPS_STATE_MASK; Here too I suppose we might do this ULPS state clearing after we've cleared DEVICE_READY. But again the device should not be in ULPS here, so it may be a bit pointless. > + temp &= ~DEVICE_READY; > + I915_WRITE(MIPI_DEVICE_READY(pipe), temp); > + msleep(5); > + I915_WRITE(MIPI_DEVICE_READY(pipe), temp | ULPS_STATE_ENTER); > +} > + > +static void intel_dsi_post_disable(struct intel_encoder *encoder) > +{ > + DRM_DEBUG_KMS("\n"); > +} > + > +static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, > + enum pipe *pipe) > +{ > + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); > + > + DRM_DEBUG_KMS("\n"); > + > + /* XXX: this is wrong */ > + if (intel_crtc) { > + u32 val; > + > + *pipe = intel_crtc->pipe; > + > + /* XXX: only works for video mode */ > + val = I915_READ(MIPI_PORT_CTRL(*pipe)); > + return val & DPI_ENABLE; > + } > + > + /* XXX: do we also need to call > + * intel_dsi->dev.dev_ops->get_hw_state(&intel_dsi->dev); > + */ > + > + return false; > +} > + > +static void intel_dsi_get_config(struct intel_encoder *encoder, > + struct intel_crtc_config *pipe_config) > +{ > + DRM_DEBUG_KMS("\n"); > + > + /* XXX: read flags, set to adjusted_mode */ > +} > + > +static int intel_dsi_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct intel_connector *intel_connector = to_intel_connector(connector); > + struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; > + struct intel_dsi *intel_dsi = intel_attached_dsi(connector); > + > + DRM_DEBUG_KMS("\n"); > + > + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { > + DRM_DEBUG_KMS("MODE_NO_DBLESCAN\n"); > + return MODE_NO_DBLESCAN; > + } > + > + if (fixed_mode) { > + if (mode->hdisplay > fixed_mode->hdisplay) > + return MODE_PANEL; > + if (mode->vdisplay > fixed_mode->vdisplay) > + return MODE_PANEL; > + } > + > + return intel_dsi->dev.dev_ops->mode_valid(&intel_dsi->dev, mode); > +} > + > +/* return pixels in terms of txbyteclkhs */ > +static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count) > +{ > + return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp, 8), lane_count); > +} > + > +static void set_dsi_timings(struct drm_encoder *encoder, > + const struct drm_display_mode *mode) > +{ > + struct drm_device *dev = encoder->dev; > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); > + struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); > + int pipe = intel_crtc->pipe; > + unsigned int bpp = intel_crtc->config.pipe_bpp; > + unsigned int lane_count = intel_dsi->lane_count; > + > + u16 hactive, hfp, hsync, hbp, vfp, vsync, vbp; > + > + hactive = mode->hdisplay; > + hfp = mode->hsync_start - mode->hdisplay; > + hsync = mode->hsync_end - mode->hsync_start; > + hbp = mode->htotal - mode->hsync_end; > + > + vfp = mode->vsync_start - mode->vdisplay; > + vsync = mode->vsync_end - mode->vsync_start; > + vbp = mode->vtotal - mode->vsync_end; > + > + /* horizontal values are in terms of high speed byte clock */ > + hactive = txbyteclkhs(hactive, bpp, lane_count); > + hfp = txbyteclkhs(hfp, bpp, lane_count); > + hsync = txbyteclkhs(hsync, bpp, lane_count); > + hbp = txbyteclkhs(hbp, bpp, lane_count); > + > + I915_WRITE(MIPI_HACTIVE_AREA_COUNT(pipe), hactive); > + I915_WRITE(MIPI_HFP_COUNT(pipe), hfp); > + > + /* meaningful for video mode non-burst sync pulse mode only, can be zero > + * for non-burst sync events and burst modes */ > + I915_WRITE(MIPI_HSYNC_PADDING_COUNT(pipe), hsync); > + I915_WRITE(MIPI_HBP_COUNT(pipe), hbp); > + > + /* vertical values are in terms of lines */ > + I915_WRITE(MIPI_VFP_COUNT(pipe), vfp); > + I915_WRITE(MIPI_VSYNC_PADDING_COUNT(pipe), vsync); > + I915_WRITE(MIPI_VBP_COUNT(pipe), vbp); > +} > + > +static void intel_dsi_mode_set(struct intel_encoder *intel_encoder) > +{ > + struct drm_encoder *encoder = &intel_encoder->base; > + struct drm_device *dev = encoder->dev; > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); > + struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); > + struct drm_display_mode *adjusted_mode = > + &intel_crtc->config.adjusted_mode; > + int pipe = intel_crtc->pipe; > + unsigned int bpp = intel_crtc->config.pipe_bpp; > + u32 val, tmp; > + > + DRM_DEBUG_KMS("pipe %d\n", pipe); > + > + /* escape clock divider, 20MHz, shared for A and C. device ready must be > + * off when doing this! txclkesc? */ > + tmp = I915_READ(MIPI_CTRL(0)); > + tmp &= ~ESCAPE_CLOCK_DIVIDER_MASK; > + I915_WRITE(MIPI_CTRL(0), tmp | ESCAPE_CLOCK_DIVIDER_1); > + > + /* read request priority is per pipe */ > + tmp = I915_READ(MIPI_CTRL(pipe)); > + tmp &= ~READ_REQUEST_PRIORITY_MASK; > + I915_WRITE(MIPI_CTRL(pipe), tmp | READ_REQUEST_PRIORITY_HIGH); > + > + /* XXX: why here, why like this? handling in irq handler?! */ > + I915_WRITE(MIPI_INTR_STAT(pipe), 0xffffffff); > + I915_WRITE(MIPI_INTR_EN(pipe), 0xffffffff); > + > + I915_WRITE(MIPI_DPHY_PARAM(pipe), > + 0x3c << EXIT_ZERO_COUNT_SHIFT | > + 0x1f << TRAIL_COUNT_SHIFT | > + 0xc5 << CLK_ZERO_COUNT_SHIFT | > + 0x1f << PREPARE_COUNT_SHIFT); > + > + I915_WRITE(MIPI_DPI_RESOLUTION(pipe), > + adjusted_mode->vdisplay << VERTICAL_ADDRESS_SHIFT | > + adjusted_mode->hdisplay << HORIZONTAL_ADDRESS_SHIFT); > + > + set_dsi_timings(encoder, adjusted_mode); > + > + val = intel_dsi->lane_count << DATA_LANES_PRG_REG_SHIFT; > + if (is_cmd_mode(intel_dsi)) { > + val |= intel_dsi->channel << CMD_MODE_CHANNEL_NUMBER_SHIFT; > + val |= CMD_MODE_DATA_WIDTH_8_BIT; /* XXX */ > + } else { > + val |= intel_dsi->channel << VID_MODE_CHANNEL_NUMBER_SHIFT; > + > + /* XXX: cross-check bpp vs. pixel format? */ > + val |= intel_dsi->pixel_format; > + } > + I915_WRITE(MIPI_DSI_FUNC_PRG(pipe), val); > + > + /* timeouts for recovery. one frame IIUC. if counter expires, EOT and > + * stop state. */ > + > + if (is_vid_mode(intel_dsi) && > + intel_dsi->video_mode_format == VIDEO_MODE_BURST) { > + I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), > + txbyteclkhs(adjusted_mode->htotal + 1, bpp, > + intel_dsi->lane_count)); Should the +1 maybe be outside the txbyteclkhs()? > + } else { > + I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), > + txbyteclkhs(adjusted_mode->vtotal * > + adjusted_mode->htotal, > + bpp, intel_dsi->lane_count)); +1 needed here too isn't it? > + } > + I915_WRITE(MIPI_LP_RX_TIMEOUT(pipe), 0xffff); Hmm. Spec says this should be somewhere between 21 and 117+max_payload_bytes*16. If we take max_payload_bytes to be 512 based on the generic data FIFO size, then we'd get 8309 as the absolute max we will ever need. > + I915_WRITE(MIPI_TURN_AROUND_TIMEOUT(pipe), 0x14); > + > + I915_WRITE(MIPI_DEVICE_RESET_TIMER(pipe), 0xffff); These two are the max according to the spec. Seems OK. > + > + /* dphy stuff */ > + > + /* in terms of low power clock */ > + I915_WRITE(MIPI_INIT_COUNT(pipe), 0x7d0); Maybe add a note that the actual initialization period is 100 usec, so people don't have to reverse compute it. Or maybe even add some txclkesc variable/define and explicitly calculate this. Maybe it would be clearer to write these timeout values in decimal, though the spec seems to prefer hex in some cases. > + > + /* recovery disables */ > + I915_WRITE(MIPI_EOT_DISABLE(pipe), intel_dsi->eot_disable); > + > + /* in terms of txbyteclkhs. actual high to low switch + > + * MIPI_STOP_STATE_STALL * MIPI_LP_BYTECLK. > + * > + * XXX: write MIPI_STOP_STATE_STALL? > + */ > + I915_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT(pipe), 0x46); > + > + /* XXX: low power clock equivalence in terms of byte clock. the number > + * of byte clocks occupied in one low power clock. based on txbyteclkhs > + * and txclkesc. txclkesc time / txbyteclk time * (105 + > + * MIPI_STOP_STATE_STALL) / 105.??? > + */ > + I915_WRITE(MIPI_LP_BYTECLK(pipe), 4); > + > + /* the bw essential for transmitting 16 long packets containing 252 > + * bytes meant for dcs write memory command is programmed in this > + * register in terms of byte clocks. based on dsi transfer rate and the > + * number of lanes configured the time taken to transmit 16 long packets > + * in a dsi stream varies. */ > + I915_WRITE(MIPI_DBI_BW_CTRL(pipe), 0x820); > + > + I915_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe), > + 0xa << LP_HS_SSW_CNT_SHIFT | > + 0x14 << HS_LP_PWR_SW_CNT_SHIFT); Hmm. Actualky calculating all this stuff might be nice. But I guess we can leave that for later. > + > + if (is_vid_mode(intel_dsi)) > + I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe), > + intel_dsi->video_mode_format); > +} > + > +static enum drm_connector_status > +intel_dsi_detect(struct drm_connector *connector, bool force) > +{ > + struct intel_dsi *intel_dsi = intel_attached_dsi(connector); > + DRM_DEBUG_KMS("\n"); > + return intel_dsi->dev.dev_ops->detect(&intel_dsi->dev); > +} > + > +static int intel_dsi_get_modes(struct drm_connector *connector) > +{ > + struct intel_connector *intel_connector = to_intel_connector(connector); > + struct drm_display_mode *mode; > + > + DRM_DEBUG_KMS("\n"); > + > + if (!intel_connector->panel.fixed_mode) { > + DRM_DEBUG_KMS("no fixed mode\n"); > + return 0; > + } > + > + mode = drm_mode_duplicate(connector->dev, > + intel_connector->panel.fixed_mode); > + if (!mode) { > + DRM_DEBUG_KMS("drm_mode_duplicate failed\n"); > + return 0; > + } > + > + drm_mode_probed_add(connector, mode); > + return 1; > +} > + > +static void intel_dsi_destroy(struct drm_connector *connector) > +{ > + struct intel_connector *intel_connector = to_intel_connector(connector); > + > + DRM_DEBUG_KMS("\n"); > + intel_panel_fini(&intel_connector->panel); > + drm_sysfs_connector_remove(connector); > + drm_connector_cleanup(connector); > + kfree(connector); > +} > + > +static const struct drm_encoder_funcs intel_dsi_funcs = { > + .destroy = intel_encoder_destroy, > +}; > + > +static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = { > + .get_modes = intel_dsi_get_modes, > + .mode_valid = intel_dsi_mode_valid, > + .best_encoder = intel_best_encoder, > +}; > + > +static const struct drm_connector_funcs intel_dsi_connector_funcs = { > + .dpms = intel_connector_dpms, > + .detect = intel_dsi_detect, > + .destroy = intel_dsi_destroy, > + .fill_modes = drm_helper_probe_single_connector_modes, > +}; > + > +bool intel_dsi_init(struct drm_device *dev) > +{ > + struct intel_dsi *intel_dsi; > + struct intel_encoder *intel_encoder; > + struct drm_encoder *encoder; > + struct intel_connector *intel_connector; > + struct drm_connector *connector; > + struct drm_display_mode *fixed_mode = NULL; > + const struct intel_dsi_device *dsi; > + unsigned int i; > + > + DRM_DEBUG_KMS("\n"); > + > + intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL); > + if (!intel_dsi) > + return false; > + > + intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); > + if (!intel_connector) { > + kfree(intel_dsi); > + return false; > + } > + > + intel_encoder = &intel_dsi->base; > + encoder = &intel_encoder->base; > + intel_dsi->attached_connector = intel_connector; > + > + connector = &intel_connector->base; > + > + drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI); > + > + /* XXX: very likely not all of these are needed */ > + intel_encoder->hot_plug = intel_dsi_hot_plug; > + intel_encoder->compute_config = intel_dsi_compute_config; > + intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable; > + intel_encoder->pre_enable = intel_dsi_pre_enable; > + intel_encoder->enable = intel_dsi_enable; > + intel_encoder->mode_set = intel_dsi_mode_set; > + intel_encoder->disable = intel_dsi_disable; > + intel_encoder->post_disable = intel_dsi_post_disable; > + intel_encoder->get_hw_state = intel_dsi_get_hw_state; > + intel_encoder->get_config = intel_dsi_get_config; > + > + intel_connector->get_hw_state = intel_connector_get_hw_state; > + > + for (i = 0; i < ARRAY_SIZE(intel_dsi_devices); i++) { > + dsi = &intel_dsi_devices[i]; > + intel_dsi->dev = *dsi; > + > + if (dsi->dev_ops->init(&intel_dsi->dev)) > + break; > + } > + > + if (i == ARRAY_SIZE(intel_dsi_devices)) { > + DRM_DEBUG_KMS("no device found\n"); > + goto err; > + } > + > + intel_encoder->type = INTEL_OUTPUT_DSI; > + intel_encoder->crtc_mask = (1 << 0); /* XXX */ Maybe just pass in "pipe" since we have DSI for both A and B. Although I didn't look at the DSI PLL code yet, so I'm not sure we can actually use both DSI outputs at the same time currently. I suppose both need to be running at the same freq since we have only one PLL, and we have to take care of the PLL sharing somewhere. > + > + intel_encoder->cloneable = false; > + drm_connector_init(dev, connector, &intel_dsi_connector_funcs, > + DRM_MODE_CONNECTOR_DSI); > + > + drm_connector_helper_add(connector, &intel_dsi_connector_helper_funcs); > + > + connector->display_info.subpixel_order = SubPixelHorizontalRGB; /*XXX*/ > + connector->interlace_allowed = false; > + connector->doublescan_allowed = false; > + > + intel_connector_attach_encoder(intel_connector, intel_encoder); > + > + drm_sysfs_connector_add(connector); > + > + fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev); > + if (!fixed_mode) { > + DRM_DEBUG_KMS("no fixed mode\n"); > + goto err; > + } > + > + fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; > + intel_panel_init(&intel_connector->panel, fixed_mode); > + > + return true; > + > +err: > + drm_encoder_cleanup(&intel_encoder->base); > + kfree(intel_dsi); > + kfree(intel_connector); > + > + return false; > +} > -- > 1.7.9.5 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Ville Syrjälä Intel OTC _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx