|
@@ -40,13 +40,59 @@
|
|
|
struct intel_sdvo_priv {
|
|
|
struct intel_i2c_chan *i2c_bus;
|
|
|
int slaveaddr;
|
|
|
+
|
|
|
+ /* Register for the SDVO device: SDVOB or SDVOC */
|
|
|
int output_device;
|
|
|
|
|
|
- u16 active_outputs;
|
|
|
+ /* Active outputs controlled by this SDVO output */
|
|
|
+ uint16_t controlled_output;
|
|
|
|
|
|
+ /*
|
|
|
+ * Capabilities of the SDVO device returned by
|
|
|
+ * i830_sdvo_get_capabilities()
|
|
|
+ */
|
|
|
struct intel_sdvo_caps caps;
|
|
|
+
|
|
|
+ /* Pixel clock limitations reported by the SDVO device, in kHz */
|
|
|
int pixel_clock_min, pixel_clock_max;
|
|
|
|
|
|
+ /**
|
|
|
+ * This is set if we're going to treat the device as TV-out.
|
|
|
+ *
|
|
|
+ * While we have these nice friendly flags for output types that ought
|
|
|
+ * to decide this for us, the S-Video output on our HDMI+S-Video card
|
|
|
+ * shows up as RGB1 (VGA).
|
|
|
+ */
|
|
|
+ bool is_tv;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This is set if we treat the device as HDMI, instead of DVI.
|
|
|
+ */
|
|
|
+ bool is_hdmi;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returned SDTV resolutions allowed for the current format, if the
|
|
|
+ * device reported it.
|
|
|
+ */
|
|
|
+ struct intel_sdvo_sdtv_resolution_reply sdtv_resolutions;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Current selected TV format.
|
|
|
+ *
|
|
|
+ * This is stored in the same structure that's passed to the device, for
|
|
|
+ * convenience.
|
|
|
+ */
|
|
|
+ struct intel_sdvo_tv_format tv_format;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * supported encoding mode, used to determine whether HDMI is
|
|
|
+ * supported
|
|
|
+ */
|
|
|
+ struct intel_sdvo_encode encode;
|
|
|
+
|
|
|
+ /* DDC bus used by this SDVO output */
|
|
|
+ uint8_t ddc_bus;
|
|
|
+
|
|
|
int save_sdvo_mult;
|
|
|
u16 save_active_outputs;
|
|
|
struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
|
|
@@ -148,8 +194,8 @@ static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr,
|
|
|
#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
|
|
|
/** Mapping of command numbers to names, for debug output */
|
|
|
const static struct _sdvo_cmd_name {
|
|
|
- u8 cmd;
|
|
|
- char *name;
|
|
|
+ u8 cmd;
|
|
|
+ char *name;
|
|
|
} sdvo_cmd_names[] = {
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
|
|
@@ -186,8 +232,35 @@ const static struct _sdvo_cmd_name {
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
|
|
|
- SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
|
|
|
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
|
|
|
+ /* HDMI op code */
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
|
|
|
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
|
|
|
};
|
|
|
|
|
|
#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
|
|
@@ -506,6 +579,50 @@ static bool intel_sdvo_set_output_timing(struct intel_output *intel_output,
|
|
|
SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
|
|
|
}
|
|
|
|
|
|
+static bool
|
|
|
+intel_sdvo_create_preferred_input_timing(struct intel_output *output,
|
|
|
+ uint16_t clock,
|
|
|
+ uint16_t width,
|
|
|
+ uint16_t height)
|
|
|
+{
|
|
|
+ struct intel_sdvo_preferred_input_timing_args args;
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ args.clock = clock;
|
|
|
+ args.width = width;
|
|
|
+ args.height = height;
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
|
|
|
+ &args, sizeof(args));
|
|
|
+ status = intel_sdvo_read_response(output, NULL, 0);
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_sdvo_get_preferred_input_timing(struct intel_output *output,
|
|
|
+ struct intel_sdvo_dtd *dtd)
|
|
|
+{
|
|
|
+ bool status;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
|
|
|
+ NULL, 0);
|
|
|
+
|
|
|
+ status = intel_sdvo_read_response(output, &dtd->part1,
|
|
|
+ sizeof(dtd->part1));
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
|
|
|
+ NULL, 0);
|
|
|
+
|
|
|
+ status = intel_sdvo_read_response(output, &dtd->part2,
|
|
|
+ sizeof(dtd->part2));
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
|
|
|
static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
|
|
|
{
|
|
@@ -536,36 +653,12 @@ static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
|
|
|
- struct drm_display_mode *mode,
|
|
|
- struct drm_display_mode *adjusted_mode)
|
|
|
-{
|
|
|
- /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
|
|
|
- * device will be told of the multiplier during mode_set.
|
|
|
- */
|
|
|
- adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-static void intel_sdvo_mode_set(struct drm_encoder *encoder,
|
|
|
- struct drm_display_mode *mode,
|
|
|
- struct drm_display_mode *adjusted_mode)
|
|
|
+static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
|
|
|
+ struct drm_display_mode *mode)
|
|
|
{
|
|
|
- struct drm_device *dev = encoder->dev;
|
|
|
- struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
- struct drm_crtc *crtc = encoder->crtc;
|
|
|
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
|
- struct intel_output *intel_output = enc_to_intel_output(encoder);
|
|
|
- struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
|
|
|
- u16 width, height;
|
|
|
- u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
|
|
|
- u16 h_sync_offset, v_sync_offset;
|
|
|
- u32 sdvox;
|
|
|
- struct intel_sdvo_dtd output_dtd;
|
|
|
- int sdvo_pixel_multiply;
|
|
|
-
|
|
|
- if (!mode)
|
|
|
- return;
|
|
|
+ uint16_t width, height;
|
|
|
+ uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
|
|
|
+ uint16_t h_sync_offset, v_sync_offset;
|
|
|
|
|
|
width = mode->crtc_hdisplay;
|
|
|
height = mode->crtc_vdisplay;
|
|
@@ -580,93 +673,423 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
|
|
|
h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
|
|
|
v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
|
|
|
|
|
|
- output_dtd.part1.clock = mode->clock / 10;
|
|
|
- output_dtd.part1.h_active = width & 0xff;
|
|
|
- output_dtd.part1.h_blank = h_blank_len & 0xff;
|
|
|
- output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
|
|
|
+ dtd->part1.clock = mode->clock / 10;
|
|
|
+ dtd->part1.h_active = width & 0xff;
|
|
|
+ dtd->part1.h_blank = h_blank_len & 0xff;
|
|
|
+ dtd->part1.h_high = (((width >> 8) & 0xf) << 4) |
|
|
|
((h_blank_len >> 8) & 0xf);
|
|
|
- output_dtd.part1.v_active = height & 0xff;
|
|
|
- output_dtd.part1.v_blank = v_blank_len & 0xff;
|
|
|
- output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
|
|
|
+ dtd->part1.v_active = height & 0xff;
|
|
|
+ dtd->part1.v_blank = v_blank_len & 0xff;
|
|
|
+ dtd->part1.v_high = (((height >> 8) & 0xf) << 4) |
|
|
|
((v_blank_len >> 8) & 0xf);
|
|
|
|
|
|
- output_dtd.part2.h_sync_off = h_sync_offset;
|
|
|
- output_dtd.part2.h_sync_width = h_sync_len & 0xff;
|
|
|
- output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
|
|
|
+ dtd->part2.h_sync_off = h_sync_offset;
|
|
|
+ dtd->part2.h_sync_width = h_sync_len & 0xff;
|
|
|
+ dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
|
|
|
(v_sync_len & 0xf);
|
|
|
- output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
|
|
|
+ dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
|
|
|
((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
|
|
|
((v_sync_len & 0x30) >> 4);
|
|
|
|
|
|
- output_dtd.part2.dtd_flags = 0x18;
|
|
|
+ dtd->part2.dtd_flags = 0x18;
|
|
|
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
|
|
|
- output_dtd.part2.dtd_flags |= 0x2;
|
|
|
+ dtd->part2.dtd_flags |= 0x2;
|
|
|
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
|
|
- output_dtd.part2.dtd_flags |= 0x4;
|
|
|
+ dtd->part2.dtd_flags |= 0x4;
|
|
|
+
|
|
|
+ dtd->part2.sdvo_flags = 0;
|
|
|
+ dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
|
|
|
+ dtd->part2.reserved = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
|
|
|
+ struct intel_sdvo_dtd *dtd)
|
|
|
+{
|
|
|
+ uint16_t width, height;
|
|
|
+ uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
|
|
|
+ uint16_t h_sync_offset, v_sync_offset;
|
|
|
+
|
|
|
+ width = mode->crtc_hdisplay;
|
|
|
+ height = mode->crtc_vdisplay;
|
|
|
+
|
|
|
+ /* do some mode translations */
|
|
|
+ h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
|
|
|
+ h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
|
|
|
+
|
|
|
+ v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
|
|
|
+ v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
|
|
|
+
|
|
|
+ h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
|
|
|
+ v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
|
|
|
+
|
|
|
+ mode->hdisplay = dtd->part1.h_active;
|
|
|
+ mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
|
|
|
+ mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off;
|
|
|
+ mode->hsync_start += (dtd->part2.sync_off_width_high & 0xa0) << 2;
|
|
|
+ mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width;
|
|
|
+ mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
|
|
|
+ mode->htotal = mode->hdisplay + dtd->part1.h_blank;
|
|
|
+ mode->htotal += (dtd->part1.h_high & 0xf) << 8;
|
|
|
+
|
|
|
+ mode->vdisplay = dtd->part1.v_active;
|
|
|
+ mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
|
|
|
+ mode->vsync_start = mode->vdisplay;
|
|
|
+ mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
|
|
|
+ mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0a) << 2;
|
|
|
+ mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0;
|
|
|
+ mode->vsync_end = mode->vsync_start +
|
|
|
+ (dtd->part2.v_sync_off_width & 0xf);
|
|
|
+ mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
|
|
|
+ mode->vtotal = mode->vdisplay + dtd->part1.v_blank;
|
|
|
+ mode->vtotal += (dtd->part1.v_high & 0xf) << 8;
|
|
|
+
|
|
|
+ mode->clock = dtd->part1.clock * 10;
|
|
|
+
|
|
|
+ mode->flags &= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
|
|
|
+ if (dtd->part2.dtd_flags & 0x2)
|
|
|
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
|
|
|
+ if (dtd->part2.dtd_flags & 0x4)
|
|
|
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_sdvo_get_supp_encode(struct intel_output *output,
|
|
|
+ struct intel_sdvo_encode *encode)
|
|
|
+{
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_SUPP_ENCODE, NULL, 0);
|
|
|
+ status = intel_sdvo_read_response(output, encode, sizeof(*encode));
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS) { /* non-support means DVI */
|
|
|
+ memset(encode, 0, sizeof(*encode));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_sdvo_set_encode(struct intel_output *output, uint8_t mode)
|
|
|
+{
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODE, &mode, 1);
|
|
|
+ status = intel_sdvo_read_response(output, NULL, 0);
|
|
|
+
|
|
|
+ return (status == SDVO_CMD_STATUS_SUCCESS);
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_sdvo_set_colorimetry(struct intel_output *output,
|
|
|
+ uint8_t mode)
|
|
|
+{
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
|
|
|
+ status = intel_sdvo_read_response(output, NULL, 0);
|
|
|
+
|
|
|
+ return (status == SDVO_CMD_STATUS_SUCCESS);
|
|
|
+}
|
|
|
+
|
|
|
+#if 0
|
|
|
+static void intel_sdvo_dump_hdmi_buf(struct intel_output *output)
|
|
|
+{
|
|
|
+ int i, j;
|
|
|
+ uint8_t set_buf_index[2];
|
|
|
+ uint8_t av_split;
|
|
|
+ uint8_t buf_size;
|
|
|
+ uint8_t buf[48];
|
|
|
+ uint8_t *pos;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_AV_SPLIT, NULL, 0);
|
|
|
+ intel_sdvo_read_response(output, &av_split, 1);
|
|
|
+
|
|
|
+ for (i = 0; i <= av_split; i++) {
|
|
|
+ set_buf_index[0] = i; set_buf_index[1] = 0;
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX,
|
|
|
+ set_buf_index, 2);
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_INFO, NULL, 0);
|
|
|
+ intel_sdvo_read_response(output, &buf_size, 1);
|
|
|
+
|
|
|
+ pos = buf;
|
|
|
+ for (j = 0; j <= buf_size; j += 8) {
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_DATA,
|
|
|
+ NULL, 0);
|
|
|
+ intel_sdvo_read_response(output, pos, 8);
|
|
|
+ pos += 8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static void intel_sdvo_set_hdmi_buf(struct intel_output *output, int index,
|
|
|
+ uint8_t *data, int8_t size, uint8_t tx_rate)
|
|
|
+{
|
|
|
+ uint8_t set_buf_index[2];
|
|
|
+
|
|
|
+ set_buf_index[0] = index;
|
|
|
+ set_buf_index[1] = 0;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2);
|
|
|
+
|
|
|
+ for (; size > 0; size -= 8) {
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_DATA, data, 8);
|
|
|
+ data += 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1);
|
|
|
+}
|
|
|
+
|
|
|
+static uint8_t intel_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size)
|
|
|
+{
|
|
|
+ uint8_t csum = 0;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < size; i++)
|
|
|
+ csum += data[i];
|
|
|
+
|
|
|
+ return 0x100 - csum;
|
|
|
+}
|
|
|
+
|
|
|
+#define DIP_TYPE_AVI 0x82
|
|
|
+#define DIP_VERSION_AVI 0x2
|
|
|
+#define DIP_LEN_AVI 13
|
|
|
+
|
|
|
+struct dip_infoframe {
|
|
|
+ uint8_t type;
|
|
|
+ uint8_t version;
|
|
|
+ uint8_t len;
|
|
|
+ uint8_t checksum;
|
|
|
+ union {
|
|
|
+ struct {
|
|
|
+ /* Packet Byte #1 */
|
|
|
+ uint8_t S:2;
|
|
|
+ uint8_t B:2;
|
|
|
+ uint8_t A:1;
|
|
|
+ uint8_t Y:2;
|
|
|
+ uint8_t rsvd1:1;
|
|
|
+ /* Packet Byte #2 */
|
|
|
+ uint8_t R:4;
|
|
|
+ uint8_t M:2;
|
|
|
+ uint8_t C:2;
|
|
|
+ /* Packet Byte #3 */
|
|
|
+ uint8_t SC:2;
|
|
|
+ uint8_t Q:2;
|
|
|
+ uint8_t EC:3;
|
|
|
+ uint8_t ITC:1;
|
|
|
+ /* Packet Byte #4 */
|
|
|
+ uint8_t VIC:7;
|
|
|
+ uint8_t rsvd2:1;
|
|
|
+ /* Packet Byte #5 */
|
|
|
+ uint8_t PR:4;
|
|
|
+ uint8_t rsvd3:4;
|
|
|
+ /* Packet Byte #6~13 */
|
|
|
+ uint16_t top_bar_end;
|
|
|
+ uint16_t bottom_bar_start;
|
|
|
+ uint16_t left_bar_end;
|
|
|
+ uint16_t right_bar_start;
|
|
|
+ } avi;
|
|
|
+ struct {
|
|
|
+ /* Packet Byte #1 */
|
|
|
+ uint8_t channel_count:3;
|
|
|
+ uint8_t rsvd1:1;
|
|
|
+ uint8_t coding_type:4;
|
|
|
+ /* Packet Byte #2 */
|
|
|
+ uint8_t sample_size:2; /* SS0, SS1 */
|
|
|
+ uint8_t sample_frequency:3;
|
|
|
+ uint8_t rsvd2:3;
|
|
|
+ /* Packet Byte #3 */
|
|
|
+ uint8_t coding_type_private:5;
|
|
|
+ uint8_t rsvd3:3;
|
|
|
+ /* Packet Byte #4 */
|
|
|
+ uint8_t channel_allocation;
|
|
|
+ /* Packet Byte #5 */
|
|
|
+ uint8_t rsvd4:3;
|
|
|
+ uint8_t level_shift:4;
|
|
|
+ uint8_t downmix_inhibit:1;
|
|
|
+ } audio;
|
|
|
+ uint8_t payload[28];
|
|
|
+ } __attribute__ ((packed)) u;
|
|
|
+} __attribute__((packed));
|
|
|
+
|
|
|
+static void intel_sdvo_set_avi_infoframe(struct intel_output *output,
|
|
|
+ struct drm_display_mode * mode)
|
|
|
+{
|
|
|
+ struct dip_infoframe avi_if = {
|
|
|
+ .type = DIP_TYPE_AVI,
|
|
|
+ .version = DIP_VERSION_AVI,
|
|
|
+ .len = DIP_LEN_AVI,
|
|
|
+ };
|
|
|
+
|
|
|
+ avi_if.checksum = intel_sdvo_calc_hbuf_csum((uint8_t *)&avi_if,
|
|
|
+ 4 + avi_if.len);
|
|
|
+ intel_sdvo_set_hdmi_buf(output, 1, (uint8_t *)&avi_if, 4 + avi_if.len,
|
|
|
+ SDVO_HBUF_TX_VSYNC);
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
|
|
|
+ struct drm_display_mode *mode,
|
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
|
+{
|
|
|
+ struct intel_output *output = enc_to_intel_output(encoder);
|
|
|
+ struct intel_sdvo_priv *dev_priv = output->dev_priv;
|
|
|
|
|
|
- output_dtd.part2.sdvo_flags = 0;
|
|
|
- output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
|
|
|
- output_dtd.part2.reserved = 0;
|
|
|
+ if (!dev_priv->is_tv) {
|
|
|
+ /* Make the CRTC code factor in the SDVO pixel multiplier. The
|
|
|
+ * SDVO device will be told of the multiplier during mode_set.
|
|
|
+ */
|
|
|
+ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
|
|
|
+ } else {
|
|
|
+ struct intel_sdvo_dtd output_dtd;
|
|
|
+ bool success;
|
|
|
+
|
|
|
+ /* We need to construct preferred input timings based on our
|
|
|
+ * output timings. To do that, we have to set the output
|
|
|
+ * timings, even though this isn't really the right place in
|
|
|
+ * the sequence to do it. Oh well.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ /* Set output timings */
|
|
|
+ intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
|
|
|
+ intel_sdvo_set_target_output(output,
|
|
|
+ dev_priv->controlled_output);
|
|
|
+ intel_sdvo_set_output_timing(output, &output_dtd);
|
|
|
+
|
|
|
+ /* Set the input timing to the screen. Assume always input 0. */
|
|
|
+ intel_sdvo_set_target_input(output, true, false);
|
|
|
+
|
|
|
+
|
|
|
+ success = intel_sdvo_create_preferred_input_timing(output,
|
|
|
+ mode->clock / 10,
|
|
|
+ mode->hdisplay,
|
|
|
+ mode->vdisplay);
|
|
|
+ if (success) {
|
|
|
+ struct intel_sdvo_dtd input_dtd;
|
|
|
|
|
|
- /* Set the output timing to the screen */
|
|
|
- intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs);
|
|
|
- intel_sdvo_set_output_timing(intel_output, &output_dtd);
|
|
|
+ intel_sdvo_get_preferred_input_timing(output,
|
|
|
+ &input_dtd);
|
|
|
+ intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void intel_sdvo_mode_set(struct drm_encoder *encoder,
|
|
|
+ struct drm_display_mode *mode,
|
|
|
+ struct drm_display_mode *adjusted_mode)
|
|
|
+{
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
|
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
|
+ struct intel_output *output = enc_to_intel_output(encoder);
|
|
|
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
|
|
|
+ u32 sdvox = 0;
|
|
|
+ int sdvo_pixel_multiply;
|
|
|
+ struct intel_sdvo_in_out_map in_out;
|
|
|
+ struct intel_sdvo_dtd input_dtd;
|
|
|
+ u8 status;
|
|
|
+
|
|
|
+ if (!mode)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* First, set the input mapping for the first input to our controlled
|
|
|
+ * output. This is only correct if we're a single-input device, in
|
|
|
+ * which case the first input is the output from the appropriate SDVO
|
|
|
+ * channel on the motherboard. In a two-input device, the first input
|
|
|
+ * will be SDVOB and the second SDVOC.
|
|
|
+ */
|
|
|
+ in_out.in0 = sdvo_priv->controlled_output;
|
|
|
+ in_out.in1 = 0;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP,
|
|
|
+ &in_out, sizeof(in_out));
|
|
|
+ status = intel_sdvo_read_response(output, NULL, 0);
|
|
|
+
|
|
|
+ if (sdvo_priv->is_hdmi) {
|
|
|
+ intel_sdvo_set_avi_infoframe(output, mode);
|
|
|
+ sdvox |= SDVO_AUDIO_ENABLE;
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_sdvo_get_dtd_from_mode(&input_dtd, mode);
|
|
|
+
|
|
|
+ /* If it's a TV, we already set the output timing in mode_fixup.
|
|
|
+ * Otherwise, the output timing is equal to the input timing.
|
|
|
+ */
|
|
|
+ if (!sdvo_priv->is_tv) {
|
|
|
+ /* Set the output timing to the screen */
|
|
|
+ intel_sdvo_set_target_output(output,
|
|
|
+ sdvo_priv->controlled_output);
|
|
|
+ intel_sdvo_set_output_timing(output, &input_dtd);
|
|
|
+ }
|
|
|
|
|
|
/* Set the input timing to the screen. Assume always input 0. */
|
|
|
- intel_sdvo_set_target_input(intel_output, true, false);
|
|
|
+ intel_sdvo_set_target_input(output, true, false);
|
|
|
|
|
|
- /* We would like to use i830_sdvo_create_preferred_input_timing() to
|
|
|
+ /* We would like to use intel_sdvo_create_preferred_input_timing() to
|
|
|
* provide the device with a timing it can support, if it supports that
|
|
|
* feature. However, presumably we would need to adjust the CRTC to
|
|
|
* output the preferred timing, and we don't support that currently.
|
|
|
*/
|
|
|
- intel_sdvo_set_input_timing(intel_output, &output_dtd);
|
|
|
+#if 0
|
|
|
+ success = intel_sdvo_create_preferred_input_timing(output, clock,
|
|
|
+ width, height);
|
|
|
+ if (success) {
|
|
|
+ struct intel_sdvo_dtd *input_dtd;
|
|
|
+
|
|
|
+ intel_sdvo_get_preferred_input_timing(output, &input_dtd);
|
|
|
+ intel_sdvo_set_input_timing(output, &input_dtd);
|
|
|
+ }
|
|
|
+#else
|
|
|
+ intel_sdvo_set_input_timing(output, &input_dtd);
|
|
|
+#endif
|
|
|
|
|
|
switch (intel_sdvo_get_pixel_multiplier(mode)) {
|
|
|
case 1:
|
|
|
- intel_sdvo_set_clock_rate_mult(intel_output,
|
|
|
+ intel_sdvo_set_clock_rate_mult(output,
|
|
|
SDVO_CLOCK_RATE_MULT_1X);
|
|
|
break;
|
|
|
case 2:
|
|
|
- intel_sdvo_set_clock_rate_mult(intel_output,
|
|
|
+ intel_sdvo_set_clock_rate_mult(output,
|
|
|
SDVO_CLOCK_RATE_MULT_2X);
|
|
|
break;
|
|
|
case 4:
|
|
|
- intel_sdvo_set_clock_rate_mult(intel_output,
|
|
|
+ intel_sdvo_set_clock_rate_mult(output,
|
|
|
SDVO_CLOCK_RATE_MULT_4X);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
/* Set the SDVO control regs. */
|
|
|
- if (0/*IS_I965GM(dev)*/) {
|
|
|
- sdvox = SDVO_BORDER_ENABLE;
|
|
|
- } else {
|
|
|
- sdvox = I915_READ(sdvo_priv->output_device);
|
|
|
- switch (sdvo_priv->output_device) {
|
|
|
- case SDVOB:
|
|
|
- sdvox &= SDVOB_PRESERVE_MASK;
|
|
|
- break;
|
|
|
- case SDVOC:
|
|
|
- sdvox &= SDVOC_PRESERVE_MASK;
|
|
|
- break;
|
|
|
- }
|
|
|
- sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
|
|
|
- }
|
|
|
+ if (IS_I965G(dev)) {
|
|
|
+ sdvox |= SDVO_BORDER_ENABLE |
|
|
|
+ SDVO_VSYNC_ACTIVE_HIGH |
|
|
|
+ SDVO_HSYNC_ACTIVE_HIGH;
|
|
|
+ } else {
|
|
|
+ sdvox |= I915_READ(sdvo_priv->output_device);
|
|
|
+ switch (sdvo_priv->output_device) {
|
|
|
+ case SDVOB:
|
|
|
+ sdvox &= SDVOB_PRESERVE_MASK;
|
|
|
+ break;
|
|
|
+ case SDVOC:
|
|
|
+ sdvox &= SDVOC_PRESERVE_MASK;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
|
|
|
+ }
|
|
|
if (intel_crtc->pipe == 1)
|
|
|
sdvox |= SDVO_PIPE_B_SELECT;
|
|
|
|
|
|
sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
|
|
|
if (IS_I965G(dev)) {
|
|
|
- /* done in crtc_mode_set as the dpll_md reg must be written
|
|
|
- early */
|
|
|
- } else if (IS_I945G(dev) || IS_I945GM(dev)) {
|
|
|
- /* done in crtc_mode_set as it lives inside the
|
|
|
- dpll register */
|
|
|
+ /* done in crtc_mode_set as the dpll_md reg must be written early */
|
|
|
+ } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
|
|
|
+ /* done in crtc_mode_set as it lives inside the dpll register */
|
|
|
} else {
|
|
|
sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
|
|
|
}
|
|
|
|
|
|
- intel_sdvo_write_sdvox(intel_output, sdvox);
|
|
|
+ intel_sdvo_write_sdvox(output, sdvox);
|
|
|
}
|
|
|
|
|
|
static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
|
|
@@ -714,7 +1137,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
|
|
|
|
|
|
if (0)
|
|
|
intel_sdvo_set_encoder_power_state(intel_output, mode);
|
|
|
- intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs);
|
|
|
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->controlled_output);
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
@@ -752,6 +1175,9 @@ static void intel_sdvo_save(struct drm_connector *connector)
|
|
|
&sdvo_priv->save_output_dtd[o]);
|
|
|
}
|
|
|
}
|
|
|
+ if (sdvo_priv->is_tv) {
|
|
|
+ /* XXX: Save TV format/enhancements. */
|
|
|
+ }
|
|
|
|
|
|
sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
|
|
|
}
|
|
@@ -759,7 +1185,6 @@ static void intel_sdvo_save(struct drm_connector *connector)
|
|
|
static void intel_sdvo_restore(struct drm_connector *connector)
|
|
|
{
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
- struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct intel_output *intel_output = to_intel_output(connector);
|
|
|
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
|
|
|
int o;
|
|
@@ -790,7 +1215,11 @@ static void intel_sdvo_restore(struct drm_connector *connector)
|
|
|
|
|
|
intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult);
|
|
|
|
|
|
- I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
|
|
|
+ if (sdvo_priv->is_tv) {
|
|
|
+ /* XXX: Restore TV format/enhancements. */
|
|
|
+ }
|
|
|
+
|
|
|
+ intel_sdvo_write_sdvox(intel_output, sdvo_priv->save_SDVOX);
|
|
|
|
|
|
if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
|
|
|
{
|
|
@@ -916,20 +1345,173 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
|
|
|
status = intel_sdvo_read_response(intel_output, &response, 2);
|
|
|
|
|
|
DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
|
|
|
+
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return connector_status_unknown;
|
|
|
+
|
|
|
if ((response[0] != 0) || (response[1] != 0))
|
|
|
return connector_status_connected;
|
|
|
else
|
|
|
return connector_status_disconnected;
|
|
|
}
|
|
|
|
|
|
-static int intel_sdvo_get_modes(struct drm_connector *connector)
|
|
|
+static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
|
|
|
{
|
|
|
struct intel_output *intel_output = to_intel_output(connector);
|
|
|
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
|
|
|
|
|
|
/* set the bus switch and get the modes */
|
|
|
- intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2);
|
|
|
+ intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus);
|
|
|
intel_ddc_get_modes(intel_output);
|
|
|
|
|
|
+#if 0
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ /* Mac mini hack. On this device, I get DDC through the analog, which
|
|
|
+ * load-detects as disconnected. I fail to DDC through the SDVO DDC,
|
|
|
+ * but it does load-detect as connected. So, just steal the DDC bits
|
|
|
+ * from analog when we fail at finding it the right way.
|
|
|
+ */
|
|
|
+ crt = xf86_config->output[0];
|
|
|
+ intel_output = crt->driver_private;
|
|
|
+ if (intel_output->type == I830_OUTPUT_ANALOG &&
|
|
|
+ crt->funcs->detect(crt) == XF86OutputStatusDisconnected) {
|
|
|
+ I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A");
|
|
|
+ edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus);
|
|
|
+ xf86DestroyI2CBusRec(intel_output->pDDCBus, true, true);
|
|
|
+ }
|
|
|
+ if (edid_mon) {
|
|
|
+ xf86OutputSetEDID(output, edid_mon);
|
|
|
+ modes = xf86OutputGetEDIDModes(output);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * This function checks the current TV format, and chooses a default if
|
|
|
+ * it hasn't been set.
|
|
|
+ */
|
|
|
+static void
|
|
|
+intel_sdvo_check_tv_format(struct intel_output *output)
|
|
|
+{
|
|
|
+ struct intel_sdvo_priv *dev_priv = output->dev_priv;
|
|
|
+ struct intel_sdvo_tv_format format, unset;
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0);
|
|
|
+ status = intel_sdvo_read_response(output, &format, sizeof(format));
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return;
|
|
|
+
|
|
|
+ memset(&unset, 0, sizeof(unset));
|
|
|
+ if (memcmp(&format, &unset, sizeof(format))) {
|
|
|
+ DRM_DEBUG("%s: Choosing default TV format of NTSC-M\n",
|
|
|
+ SDVO_NAME(dev_priv));
|
|
|
+
|
|
|
+ format.ntsc_m = true;
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, NULL, 0);
|
|
|
+ status = intel_sdvo_read_response(output, NULL, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set of SDVO TV modes.
|
|
|
+ * Note! This is in reply order (see loop in get_tv_modes).
|
|
|
+ * XXX: all 60Hz refresh?
|
|
|
+ */
|
|
|
+struct drm_display_mode sdvo_tv_modes[] = {
|
|
|
+ { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815680, 321, 384, 416,
|
|
|
+ 200, 0, 232, 201, 233, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814080, 321, 384, 416,
|
|
|
+ 240, 0, 272, 241, 273, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910080, 401, 464, 496,
|
|
|
+ 300, 0, 332, 301, 333, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913280, 641, 704, 736,
|
|
|
+ 350, 0, 382, 351, 383, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736,
|
|
|
+ 400, 0, 432, 401, 433, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736,
|
|
|
+ 400, 0, 432, 401, 433, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624000, 705, 768, 800,
|
|
|
+ 480, 0, 512, 481, 513, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232000, 705, 768, 800,
|
|
|
+ 576, 0, 608, 577, 609, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751680, 721, 784, 816,
|
|
|
+ 350, 0, 382, 351, 383, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199680, 721, 784, 816,
|
|
|
+ 400, 0, 432, 401, 433, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116480, 721, 784, 816,
|
|
|
+ 480, 0, 512, 481, 513, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054080, 721, 784, 816,
|
|
|
+ 540, 0, 572, 541, 573, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816640, 721, 784, 816,
|
|
|
+ 576, 0, 608, 577, 609, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570560, 769, 832, 864,
|
|
|
+ 576, 0, 608, 577, 609, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030080, 801, 864, 896,
|
|
|
+ 600, 0, 632, 601, 633, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581760, 833, 896, 928,
|
|
|
+ 624, 0, 656, 625, 657, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707040, 921, 984, 1016,
|
|
|
+ 766, 0, 798, 767, 799, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827200, 1025, 1088, 1120,
|
|
|
+ 768, 0, 800, 769, 801, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265920, 1281, 1344, 1376,
|
|
|
+ 1024, 0, 1056, 1025, 1057, 4196112, 0,
|
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
+};
|
|
|
+
|
|
|
+static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
|
|
|
+{
|
|
|
+ struct intel_output *output = to_intel_output(connector);
|
|
|
+ uint32_t reply = 0;
|
|
|
+ uint8_t status;
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ intel_sdvo_check_tv_format(output);
|
|
|
+
|
|
|
+ /* Read the list of supported input resolutions for the selected TV
|
|
|
+ * format.
|
|
|
+ */
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
|
|
|
+ NULL, 0);
|
|
|
+ status = intel_sdvo_read_response(output, &reply, 3);
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++)
|
|
|
+ if (reply & (1 << i))
|
|
|
+ drm_mode_probed_add(connector, &sdvo_tv_modes[i]);
|
|
|
+}
|
|
|
+
|
|
|
+static int intel_sdvo_get_modes(struct drm_connector *connector)
|
|
|
+{
|
|
|
+ struct intel_output *output = to_intel_output(connector);
|
|
|
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
|
|
|
+
|
|
|
+ if (sdvo_priv->is_tv)
|
|
|
+ intel_sdvo_get_tv_modes(connector);
|
|
|
+ else
|
|
|
+ intel_sdvo_get_ddc_modes(connector);
|
|
|
+
|
|
|
if (list_empty(&connector->probed_modes))
|
|
|
return 0;
|
|
|
return 1;
|
|
@@ -978,6 +1560,65 @@ static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
|
|
|
};
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * Choose the appropriate DDC bus for control bus switch command for this
|
|
|
+ * SDVO output based on the controlled output.
|
|
|
+ *
|
|
|
+ * DDC bus number assignment is in a priority order of RGB outputs, then TMDS
|
|
|
+ * outputs, then LVDS outputs.
|
|
|
+ */
|
|
|
+static void
|
|
|
+intel_sdvo_select_ddc_bus(struct intel_sdvo_priv *dev_priv)
|
|
|
+{
|
|
|
+ uint16_t mask = 0;
|
|
|
+ unsigned int num_bits;
|
|
|
+
|
|
|
+ /* Make a mask of outputs less than or equal to our own priority in the
|
|
|
+ * list.
|
|
|
+ */
|
|
|
+ switch (dev_priv->controlled_output) {
|
|
|
+ case SDVO_OUTPUT_LVDS1:
|
|
|
+ mask |= SDVO_OUTPUT_LVDS1;
|
|
|
+ case SDVO_OUTPUT_LVDS0:
|
|
|
+ mask |= SDVO_OUTPUT_LVDS0;
|
|
|
+ case SDVO_OUTPUT_TMDS1:
|
|
|
+ mask |= SDVO_OUTPUT_TMDS1;
|
|
|
+ case SDVO_OUTPUT_TMDS0:
|
|
|
+ mask |= SDVO_OUTPUT_TMDS0;
|
|
|
+ case SDVO_OUTPUT_RGB1:
|
|
|
+ mask |= SDVO_OUTPUT_RGB1;
|
|
|
+ case SDVO_OUTPUT_RGB0:
|
|
|
+ mask |= SDVO_OUTPUT_RGB0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Count bits to find what number we are in the priority list. */
|
|
|
+ mask &= dev_priv->caps.output_flags;
|
|
|
+ num_bits = hweight16(mask);
|
|
|
+ if (num_bits > 3) {
|
|
|
+ /* if more than 3 outputs, default to DDC bus 3 for now */
|
|
|
+ num_bits = 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Corresponds to SDVO_CONTROL_BUS_DDCx */
|
|
|
+ dev_priv->ddc_bus = 1 << num_bits;
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+intel_sdvo_get_digital_encoding_mode(struct intel_output *output)
|
|
|
+{
|
|
|
+ struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
|
|
|
+ uint8_t status;
|
|
|
+
|
|
|
+ intel_sdvo_set_target_output(output, sdvo_priv->controlled_output);
|
|
|
+
|
|
|
+ intel_sdvo_write_cmd(output, SDVO_CMD_GET_ENCODE, NULL, 0);
|
|
|
+ status = intel_sdvo_read_response(output, &sdvo_priv->is_hdmi, 1);
|
|
|
+ if (status != SDVO_CMD_STATUS_SUCCESS)
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
bool intel_sdvo_init(struct drm_device *dev, int output_device)
|
|
|
{
|
|
|
struct drm_connector *connector;
|
|
@@ -1040,45 +1681,76 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
|
|
|
|
|
|
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
|
|
|
|
|
|
- memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
|
|
|
+ if (sdvo_priv->caps.output_flags &
|
|
|
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
|
|
|
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
|
|
|
+ else
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
|
|
|
+
|
|
|
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
+ encoder_type = DRM_MODE_ENCODER_TMDS;
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_DVID;
|
|
|
|
|
|
- /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
|
|
|
- if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
|
|
|
+ if (intel_sdvo_get_supp_encode(intel_output,
|
|
|
+ &sdvo_priv->encode) &&
|
|
|
+ intel_sdvo_get_digital_encoding_mode(intel_output) &&
|
|
|
+ sdvo_priv->is_hdmi) {
|
|
|
+ /* enable hdmi encoding mode if supported */
|
|
|
+ intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
|
|
|
+ intel_sdvo_set_colorimetry(intel_output,
|
|
|
+ SDVO_COLORIMETRY_RGB256);
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_HDMIA;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0)
|
|
|
{
|
|
|
- sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
|
|
|
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
+ encoder_type = DRM_MODE_ENCODER_TVDAC;
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_SVIDEO;
|
|
|
+ sdvo_priv->is_tv = true;
|
|
|
+ intel_output->needs_tv_clock = true;
|
|
|
+ }
|
|
|
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
|
|
|
+ {
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
|
|
|
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
encoder_type = DRM_MODE_ENCODER_DAC;
|
|
|
connector_type = DRM_MODE_CONNECTOR_VGA;
|
|
|
}
|
|
|
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
|
|
|
{
|
|
|
- sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
|
|
|
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
encoder_type = DRM_MODE_ENCODER_DAC;
|
|
|
connector_type = DRM_MODE_CONNECTOR_VGA;
|
|
|
}
|
|
|
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
|
|
|
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0)
|
|
|
{
|
|
|
- sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
|
|
|
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
- encoder_type = DRM_MODE_ENCODER_TMDS;
|
|
|
- connector_type = DRM_MODE_CONNECTOR_DVID;
|
|
|
+ encoder_type = DRM_MODE_ENCODER_LVDS;
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_LVDS;
|
|
|
}
|
|
|
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
|
|
|
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1)
|
|
|
{
|
|
|
- sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
|
|
|
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
|
|
|
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
|
|
- encoder_type = DRM_MODE_ENCODER_TMDS;
|
|
|
- connector_type = DRM_MODE_CONNECTOR_DVID;
|
|
|
+ encoder_type = DRM_MODE_ENCODER_LVDS;
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_LVDS;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
unsigned char bytes[2];
|
|
|
|
|
|
+ sdvo_priv->controlled_output = 0;
|
|
|
memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
|
|
|
- DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
|
|
|
+ DRM_DEBUG("%s: Unknown SDVO output type (0x%02x%02x)\n",
|
|
|
SDVO_NAME(sdvo_priv),
|
|
|
bytes[0], bytes[1]);
|
|
|
+ encoder_type = DRM_MODE_ENCODER_NONE;
|
|
|
+ connector_type = DRM_MODE_CONNECTOR_Unknown;
|
|
|
goto err_i2c;
|
|
|
}
|
|
|
|
|
@@ -1089,6 +1761,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
|
|
|
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
|
|
|
drm_sysfs_connector_add(connector);
|
|
|
|
|
|
+ intel_sdvo_select_ddc_bus(sdvo_priv);
|
|
|
+
|
|
|
/* Set the input timing to the screen. Assume always input 0. */
|
|
|
intel_sdvo_set_target_input(intel_output, true, false);
|
|
|
|